Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

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

Open
seanm opened this issue Jun 3, 2024 · 1 comment
Labels
type:Bug Inconsistencies or issues which will cause an incorrect result under some or all circumstances

Comments

@seanm
Copy link
Contributor

seanm commented Jun 3, 2024

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 seanm added the type:Bug Inconsistencies or issues which will cause an incorrect result under some or all circumstances label Jun 3, 2024
@seanm
Copy link
Contributor Author

seanm commented Jun 3, 2024

@blowekamp maybe this is similar to #3031 ?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
type:Bug Inconsistencies or issues which will cause an incorrect result under some or all circumstances
Projects
None yet
Development

No branches or pull requests

1 participant