InsightSoftwareConsortium / InsightSoftwareConsortium/ITK

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

Open
#4,709 2 comments 0 reactions 0 assignees View on GitHub
type:Bug
Dominant language
C++
Stars
1.7k
Forks
748
Avg merge
1d 1h
Merged PRs (30d)
64

Description

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>::ThreadedApplyUpdate(double const&, itk::NarrowBandImageFilterBase, itk::Image>::ThreadRegionType const&, unsigned int) itkNarrowBandImageFilterBase.hxx:234 (ITKNarrowBandTestDriver:arm64+0x10004307c)
#1 itk::NarrowBandImageFilterBase, itk::Image>::GenerateData()::'lambda0'(unsigned long)::operator()(unsigned long) const itkNarrowBandImageFilterBase.hxx:132 (ITKNarrowBandTestDriver:arm64+0x100053698)
#2 decltype(std::declval, itk::Image>::GenerateData()::'lambda0'(unsigned long)&>()(std::declval())) std::__1::__invoke[abi:ue170006], itk::Image>::GenerateData()::'lambda0'(unsigned long)&, unsigned long>(itk::NarrowBandImageFilterBase, itk::Image>::GenerateData()::'lambda0'(unsigned long)&, unsigned long&&) invoke.h:340 (ITKNarrowBandTestDriver:arm64+0x1000535b4)
#3 void std::__1::__invoke_void_return_wrapper::__call[abi:ue170006], itk::Image>::GenerateData()::'lambda0'(unsigned long)&, unsigned long>(itk::NarrowBandImageFilterBase, itk::Image>::GenerateData()::'lambda0'(unsigned long)&, unsigned long&&) invoke.h:415 (ITKNarrowBandTestDriver:arm64+0x1000534f0)
#4 std::__1::__function::__alloc_func, itk::Image>::GenerateData()::'lambda0'(unsigned long), std::__1::allocator, itk::Image>::GenerateData()::'lambda0'(unsigned long)>, void (unsigned long)>::operator()[abi:ue170006](unsigned long&&) function.h:193 (ITKNarrowBandTestDriver:arm64+0x10005348c)
#5 std::__1::__function::__func, itk::Image>::GenerateData()::'lambda0'(unsigned long), std::__1::allocator, itk::Image>::GenerateData()::'lambda0'(unsigned long)>, void (unsigned long)>::operator()(unsigned long&&) function.h:364 (ITKNarrowBandTestDriver:arm64+0x100051730)
#6 std::__1::__function::__value_func::operator()[abi:ue170006](unsigned long&&) const function.h:518 (ITKNarrowBandTestDriver:arm64+0x100e1b52c)
#7 std::__1::function::operator()(unsigned long) const function.h:1169 (ITKNarrowBandTestDriver:arm64+0x100e16324)
#8 itk::PoolMultiThreader::ParallelizeArray(unsigned long, unsigned long, std::__1::function, itk::ProcessObject*)::$_2::operator()(unsigned long, unsigned long) const itkPoolMultiThreader.cxx:178 (ITKNarrowBandTestDriver:arm64+0x100e8e374)

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

```

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:

```c++
template
void
NarrowBandImageFilterBase::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?

Contributor guide

Open the contributing guide

Assessment

This issue has not been assessed yet.

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.