InsightSoftwareConsortium / ITK

Insight Toolkit (ITK) -- Official Repository. ITK builds on a proven, spatially-oriented architecture for processing, segmentation, and registration of scientific images in two, three, or more dimensions.
https://itk.org
Apache License 2.0
1.37k stars 660 forks source link

itkNarrowBandImageFilterBaseTest read/write race with GetPixel() / SetPixel() #4709

Open seanm opened 4 weeks ago

seanm commented 4 weeks ago

Running the itkNarrowBandImageFilterBaseTest test with TSan:

WARNING: ThreadSanitizer: data race (pid=71194)
  Read of size 4 at 0x000102c13fac by thread T13:
    #0 itk::NarrowBandImageFilterBase<itk::Image<float, 2u>, itk::Image<float, 2u>>::ThreadedApplyUpdate(double const&, itk::NarrowBandImageFilterBase<itk::Image<float, 2u>, itk::Image<float, 2u>>::ThreadRegionType const&, unsigned int) itkNarrowBandImageFilterBase.hxx:234 (ITKNarrowBandTestDriver:arm64+0x10004307c)
    #1 itk::NarrowBandImageFilterBase<itk::Image<float, 2u>, itk::Image<float, 2u>>::GenerateData()::'lambda0'(unsigned long)::operator()(unsigned long) const itkNarrowBandImageFilterBase.hxx:132 (ITKNarrowBandTestDriver:arm64+0x100053698)
    #2 decltype(std::declval<itk::NarrowBandImageFilterBase<itk::Image<float, 2u>, itk::Image<float, 2u>>::GenerateData()::'lambda0'(unsigned long)&>()(std::declval<unsigned long>())) std::__1::__invoke[abi:ue170006]<itk::NarrowBandImageFilterBase<itk::Image<float, 2u>, itk::Image<float, 2u>>::GenerateData()::'lambda0'(unsigned long)&, unsigned long>(itk::NarrowBandImageFilterBase<itk::Image<float, 2u>, itk::Image<float, 2u>>::GenerateData()::'lambda0'(unsigned long)&, unsigned long&&) invoke.h:340 (ITKNarrowBandTestDriver:arm64+0x1000535b4)
    #3 void std::__1::__invoke_void_return_wrapper<void, true>::__call[abi:ue170006]<itk::NarrowBandImageFilterBase<itk::Image<float, 2u>, itk::Image<float, 2u>>::GenerateData()::'lambda0'(unsigned long)&, unsigned long>(itk::NarrowBandImageFilterBase<itk::Image<float, 2u>, itk::Image<float, 2u>>::GenerateData()::'lambda0'(unsigned long)&, unsigned long&&) invoke.h:415 (ITKNarrowBandTestDriver:arm64+0x1000534f0)
    #4 std::__1::__function::__alloc_func<itk::NarrowBandImageFilterBase<itk::Image<float, 2u>, itk::Image<float, 2u>>::GenerateData()::'lambda0'(unsigned long), std::__1::allocator<itk::NarrowBandImageFilterBase<itk::Image<float, 2u>, itk::Image<float, 2u>>::GenerateData()::'lambda0'(unsigned long)>, void (unsigned long)>::operator()[abi:ue170006](unsigned long&&) function.h:193 (ITKNarrowBandTestDriver:arm64+0x10005348c)
    #5 std::__1::__function::__func<itk::NarrowBandImageFilterBase<itk::Image<float, 2u>, itk::Image<float, 2u>>::GenerateData()::'lambda0'(unsigned long), std::__1::allocator<itk::NarrowBandImageFilterBase<itk::Image<float, 2u>, itk::Image<float, 2u>>::GenerateData()::'lambda0'(unsigned long)>, void (unsigned long)>::operator()(unsigned long&&) function.h:364 (ITKNarrowBandTestDriver:arm64+0x100051730)
    #6 std::__1::__function::__value_func<void (unsigned long)>::operator()[abi:ue170006](unsigned long&&) const function.h:518 (ITKNarrowBandTestDriver:arm64+0x100e1b52c)
    #7 std::__1::function<void (unsigned long)>::operator()(unsigned long) const function.h:1169 (ITKNarrowBandTestDriver:arm64+0x100e16324)
    #8 itk::PoolMultiThreader::ParallelizeArray(unsigned long, unsigned long, std::__1::function<void (unsigned long)>, itk::ProcessObject*)::$_2::operator()(unsigned long, unsigned long) const itkPoolMultiThreader.cxx:178 (ITKNarrowBandTestDriver:arm64+0x100e8e374)
    <snip>

  Previous write of size 4 at 0x000102c13fac by thread T15:
    #0 itk::Image<float, 2u>::SetPixel(itk::Index<2u> const&, float const&) itkImage.h:211 (ITKNarrowBandTestDriver:arm64+0x10005da60)
    #1 itk::NarrowBandImageFilterBase<itk::Image<float, 2u>, itk::Image<float, 2u>>::ThreadedApplyUpdate(double const&, itk::NarrowBandImageFilterBase<itk::Image<float, 2u>, itk::Image<float, 2u>>::ThreadRegionType const&, unsigned int) itkNarrowBandImageFilterBase.hxx:239 (ITKNarrowBandTestDriver:arm64+0x1000431f0)
    #2 itk::NarrowBandImageFilterBase<itk::Image<float, 2u>, itk::Image<float, 2u>>::GenerateData()::'lambda0'(unsigned long)::operator()(unsigned long) const itkNarrowBandImageFilterBase.hxx:132 (ITKNarrowBandTestDriver:arm64+0x100053698)
    #3 decltype(std::declval<itk::NarrowBandImageFilterBase<itk::Image<float, 2u>, itk::Image<float, 2u>>::GenerateData()::'lambda0'(unsigned long)&>()(std::declval<unsigned long>())) std::__1::__invoke[abi:ue170006]<itk::NarrowBandImageFilterBase<itk::Image<float, 2u>, itk::Image<float, 2u>>::GenerateData()::'lambda0'(unsigned long)&, unsigned long>(itk::NarrowBandImageFilterBase<itk::Image<float, 2u>, itk::Image<float, 2u>>::GenerateData()::'lambda0'(unsigned long)&, unsigned long&&) invoke.h:340 (ITKNarrowBandTestDriver:arm64+0x1000535b4)
    #4 void std::__1::__invoke_void_return_wrapper<void, true>::__call[abi:ue170006]<itk::NarrowBandImageFilterBase<itk::Image<float, 2u>, itk::Image<float, 2u>>::GenerateData()::'lambda0'(unsigned long)&, unsigned long>(itk::NarrowBandImageFilterBase<itk::Image<float, 2u>, itk::Image<float, 2u>>::GenerateData()::'lambda0'(unsigned long)&, unsigned long&&) invoke.h:415 (ITKNarrowBandTestDriver:arm64+0x1000534f0)
    #5 std::__1::__function::__alloc_func<itk::NarrowBandImageFilterBase<itk::Image<float, 2u>, itk::Image<float, 2u>>::GenerateData()::'lambda0'(unsigned long), std::__1::allocator<itk::NarrowBandImageFilterBase<itk::Image<float, 2u>, itk::Image<float, 2u>>::GenerateData()::'lambda0'(unsigned long)>, void (unsigned long)>::operator()[abi:ue170006](unsigned long&&) function.h:193 (ITKNarrowBandTestDriver:arm64+0x10005348c)
    #6 std::__1::__function::__func<itk::NarrowBandImageFilterBase<itk::Image<float, 2u>, itk::Image<float, 2u>>::GenerateData()::'lambda0'(unsigned long), std::__1::allocator<itk::NarrowBandImageFilterBase<itk::Image<float, 2u>, itk::Image<float, 2u>>::GenerateData()::'lambda0'(unsigned long)>, void (unsigned long)>::operator()(unsigned long&&) function.h:364 (ITKNarrowBandTestDriver:arm64+0x100051730)
    #7 std::__1::__function::__value_func<void (unsigned long)>::operator()[abi:ue170006](unsigned long&&) const function.h:518 (ITKNarrowBandTestDriver:arm64+0x100e1b52c)
    #8 std::__1::function<void (unsigned long)>::operator()(unsigned long) const function.h:1169 (ITKNarrowBandTestDriver:arm64+0x100e16324)
    #9 itk::PoolMultiThreader::ParallelizeArray(unsigned long, unsigned long, std::__1::function<void (unsigned long)>, itk::ProcessObject*)::$_2::operator()(unsigned long, unsigned long) const itkPoolMultiThreader.cxx:178 (ITKNarrowBandTestDriver:arm64+0x100e8e374)
    <snip>

We see from the above that one thread is writing to 0x000102c13fac and another is simultaneously reading from the same address.

Here is the relevant code, NB the fire emojis:

template <typename TInputImage, typename TOutputImage>
void
NarrowBandImageFilterBase<TInputImage, TOutputImage>::ThreadedApplyUpdate(const TimeStepType &     dt,
                                                                          const ThreadRegionType & regionToProcess,
                                                                          ThreadIdType             threadId)
{
  // const int INNER_MASK = 2;
  constexpr signed char INNER_MASK = 2;

  typename NarrowBandType::ConstIterator it;
  typename OutputImageType::Pointer      image = this->GetOutput();
  typename OutputImageType::PixelType    oldvalue;
  typename OutputImageType::PixelType    newvalue;
  for (it = regionToProcess.first; it != regionToProcess.last; ++it)
  {
    oldvalue = image->GetPixel(it->m_Index);//READ πŸ”₯πŸ”₯πŸ”₯πŸ”₯
    newvalue = oldvalue + dt * it->m_Data;
    // Check whether solution is out the inner band or not
    m_TouchedForThread[threadId] =
      (m_TouchedForThread[threadId] || (!(it->m_NodeState & INNER_MASK) && ((oldvalue > 0) != (newvalue > 0))));
    image->SetPixel(it->m_Index, newvalue);//WRITE πŸ”₯πŸ”₯πŸ”₯πŸ”₯
  }
}

I don't know this code at all, but given the names PoolMultiThreader::ParallelizeArray and ThreadedApplyUpdate this looks like different threads are supposed to be dealing with different regions, but I'd guess perhaps the regions are overlapping?

seanm commented 4 weeks ago

@blowekamp maybe this is similar to https://github.com/InsightSoftwareConsortium/ITK/issues/3031 ?