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

BUG: Address race condition with threaded copying input to output #4988

Conversation

blowekamp
Copy link
Member

Move copying input to output to before threaded methods, to ensure the operation has been completed before modifications.

Addresses #4969

PR Checklist

  • No API changes were made (or the changes have been approved)
  • No major design changes were made (or the changes have been approved)
  • Added test (or behavior not changed)
  • Updated API documentation (or API not changed)
  • Added license to new files (if any)
  • Added Python wrapping to new files (if any) as described in ITK Software Guide Section 9.5
  • Added ITK examples for all new major features (if any)

Refer to the ITK Software Guide for
further development details if necessary.

Move copying input to output to before threaded methods, to ensure
the operation has been completed before modifications.
@github-actions github-actions bot added type:Bug Inconsistencies or issues which will cause an incorrect result under some or all circumstances area:Filtering Issues affecting the Filtering module labels Nov 25, 2024
@blowekamp
Copy link
Member Author

After reviewing the algorithm again, the approach does not produce output that is consistent with standard morphological operators.

@N-Dekker
Copy link
Contributor

N-Dekker commented Nov 25, 2024

Trying to understand what it's actually doing here: It fills the entire output buffer with either {0, 0, 0, ...} or {1, 1, 1, ...}, depending on m_ObjectValue. Then it asks for each and every output pixel: "are you not-exactly-equal-to m_ObjectValue?". Couldn't that question if (Math::NotExactlyEquals(oRegIter.Get(), m_ObjectValue)) be taken out of the while loop?

if (Math::ExactlyEquals(m_ObjectValue, typename TInputImage::PixelType{}))
{
this->GetOutput()->FillBuffer(1);
}
else
{
this->GetOutput()->FillBuffer(0);
}
RegionType requestedRegion = this->GetOutput()->GetRequestedRegion();
auto iRegIter = ImageRegionConstIterator<InputImageType>(this->GetInput(), requestedRegion);
auto oRegIter = ImageRegionIterator<OutputImageType>(this->GetOutput(), requestedRegion);
/* Copy the input image to the output image - then only boundary pixels
* need to be changed in the output image */
while (!oRegIter.IsAtEnd())
{
if (Math::NotExactlyEquals(oRegIter.Get(), m_ObjectValue))
{
oRegIter.Set(iRegIter.Get());
}
++oRegIter;
++iRegIter;
}
}

@blowekamp
Copy link
Member Author

Trying to understand what it's actually doing here: It fills the entire output buffer with either {0, 0, 0, ...} or {1, 1, 1, ...}, depending on m_ObjectValue. Then it asks for each and every output pixel: are you not-exactly-equal-to m_ObjectValue. Couldn't that question if (Math::NotExactlyEquals(oRegIter.Get(), m_ObjectValue)) be taken out of the while loop?

if (Math::ExactlyEquals(m_ObjectValue, typename TInputImage::PixelType{}))
{
this->GetOutput()->FillBuffer(1);
}
else
{
this->GetOutput()->FillBuffer(0);
}
RegionType requestedRegion = this->GetOutput()->GetRequestedRegion();
auto iRegIter = ImageRegionConstIterator<InputImageType>(this->GetInput(), requestedRegion);
auto oRegIter = ImageRegionIterator<OutputImageType>(this->GetOutput(), requestedRegion);
/* Copy the input image to the output image - then only boundary pixels
* need to be changed in the output image */
while (!oRegIter.IsAtEnd())
{
if (Math::NotExactlyEquals(oRegIter.Get(), m_ObjectValue))
{
oRegIter.Set(iRegIter.Get());
}
++oRegIter;
++iRegIter;
}
}

There are a lot of things in this filter that I am not following why they are don't they way they are or if they are correct.

If what you are suggesting is correct then why just wan't the input copied to the output? Or why doesn't the pseudo-morphological operation that the input image as input?

Some one could be using the filter or it could just be wrong 🤷

This change just fixes the race condition.

@N-Dekker
Copy link
Contributor

Should we then add a warning note to the documentation, saying that the behavior of this filter is a bit unclear?

@seanm
Copy link
Contributor

seanm commented Nov 25, 2024

@blowekamp thanks for working on this!

You may have indeed fixed this write-write race, but the test still fails under TSan, now with a read-write race:

(lldb) r
Process 41936 launched: '/Users/sean/external/ITK-big-bin/Wrapping/Generators/Python/itk/ITKLevelSetsTestDriver' (arm64)
ITKLevelSetsTestDriver(41936,0x202738f40) malloc: nano zone abandoned due to inability to reserve vm space.
Trying mf->Update()
==================
WARNING: ThreadSanitizer: data race (pid=41936)
  Read of size 1 at 0x00010b524516 by thread T19:
    #0 itk::ParallelSparseFieldLevelSetImageFilter<itk::Image<float, 3u>, itk::Image<float, 3u>>::ThreadedUpdateActiveLayerValues(double const&, itk::SparseFieldLayer<itk::ParallelSparseFieldLevelSetNode<itk::Index<3u>>>*, itk::SparseFieldLayer<itk::ParallelSparseFieldLevelSetNode<itk::Index<3u>>>*, unsigned int) itkParallelSparseFieldLevelSetImageFilter.hxx:1556 (ITKLevelSetsTestDriver:arm64+0x1002f95bc)
    #1 itk::ParallelSparseFieldLevelSetImageFilter<itk::Image<float, 3u>, itk::Image<float, 3u>>::ThreadedApplyUpdate(double const&, unsigned int) itkParallelSparseFieldLevelSetImageFilter.hxx:1374 (ITKLevelSetsTestDriver:arm64+0x1002c80fc)
    #2 itk::ParallelSparseFieldLevelSetImageFilter<itk::Image<float, 3u>, itk::Image<float, 3u>>::Iterate()::'lambda1'(unsigned long)::operator()(unsigned long) const itkParallelSparseFieldLevelSetImageFilter.hxx:1210 (ITKLevelSetsTestDriver:arm64+0x1002eb744)
    #3 decltype(std::declval<itk::ParallelSparseFieldLevelSetImageFilter<itk::Image<float, 3u>, itk::Image<float, 3u>>::Iterate()::'lambda1'(unsigned long)&>()(std::declval<unsigned long>())) std::__1::__invoke[abi:ue170006]<itk::ParallelSparseFieldLevelSetImageFilter<itk::Image<float, 3u>, itk::Image<float, 3u>>::Iterate()::'lambda1'(unsigned long)&, unsigned long>(itk::ParallelSparseFieldLevelSetImageFilter<itk::Image<float, 3u>, itk::Image<float, 3u>>::Iterate()::'lambda1'(unsigned long)&, unsigned long&&) invoke.h:340 (ITKLevelSetsTestDriver:arm64+0x1002eb6ac)
    #4 void std::__1::__invoke_void_return_wrapper<void, true>::__call[abi:ue170006]<itk::ParallelSparseFieldLevelSetImageFilter<itk::Image<float, 3u>, itk::Image<float, 3u>>::Iterate()::'lambda1'(unsigned long)&, unsigned long>(itk::ParallelSparseFieldLevelSetImageFilter<itk::Image<float, 3u>, itk::Image<float, 3u>>::Iterate()::'lambda1'(unsigned long)&, unsigned long&&) invoke.h:415 (ITKLevelSetsTestDriver:arm64+0x1002eb5e8)
    #5 std::__1::__function::__alloc_func<itk::ParallelSparseFieldLevelSetImageFilter<itk::Image<float, 3u>, itk::Image<float, 3u>>::Iterate()::'lambda1'(unsigned long), std::__1::allocator<itk::ParallelSparseFieldLevelSetImageFilter<itk::Image<float, 3u>, itk::Image<float, 3u>>::Iterate()::'lambda1'(unsigned long)>, void (unsigned long)>::operator()[abi:ue170006](unsigned long&&) function.h:193 (ITKLevelSetsTestDriver:arm64+0x1002eb584)
    #6 std::__1::__function::__func<itk::ParallelSparseFieldLevelSetImageFilter<itk::Image<float, 3u>, itk::Image<float, 3u>>::Iterate()::'lambda1'(unsigned long), std::__1::allocator<itk::ParallelSparseFieldLevelSetImageFilter<itk::Image<float, 3u>, itk::Image<float, 3u>>::Iterate()::'lambda1'(unsigned long)>, void (unsigned long)>::operator()(unsigned long&&) function.h:364 (ITKLevelSetsTestDriver:arm64+0x1002e94bc)
    #7 std::__1::__function::__value_func<void (unsigned long)>::operator()[abi:ue170006](unsigned long&&) const function.h:518 (ITKLevelSetsTestDriver:arm64+0x1011cad4c)
    #8 std::__1::function<void (unsigned long)>::operator()(unsigned long) const function.h:1169 (ITKLevelSetsTestDriver:arm64+0x1011c5e10)
    #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 (ITKLevelSetsTestDriver:arm64+0x10123e10c)
    #10 std::__1::future<std::__1::invoke_result<itk::PoolMultiThreader::ParallelizeArray(unsigned long, unsigned long, std::__1::function<void (unsigned long)>, itk::ProcessObject*)::$_2&, unsigned long&, unsigned long const&>::type> itk::ThreadPool::AddWork<itk::PoolMultiThreader::ParallelizeArray(unsigned long, unsigned long, std::__1::function<void (unsigned long)>, itk::ProcessObject*)::$_2&, unsigned long&, unsigned long const&>(itk::PoolMultiThreader::ParallelizeArray(unsigned long, unsigned long, std::__1::function<void (unsigned long)>, itk::ProcessObject*)::$_2&, unsigned long&, unsigned long const&)::'lambda'()::operator()() const itkThreadPool.h:92 (ITKLevelSetsTestDriver:arm64+0x10123e068)
    #11 decltype(std::declval<itk::PoolMultiThreader::ParallelizeArray(unsigned long, unsigned long, std::__1::function<void (unsigned long)>, itk::ProcessObject*)::$_2&>()(std::declval<unsigned long&>(), std::declval<unsigned long const&>())) std::__1::__invoke[abi:ue170006]<std::__1::future<std::__1::invoke_result<itk::PoolMultiThreader::ParallelizeArray(unsigned long, unsigned long, std::__1::function<void (unsigned long)>, itk::ProcessObject*)::$_2&, unsigned long&, unsigned long const&>::type> itk::ThreadPool::AddWork<itk::PoolMultiThreader::ParallelizeArray(unsigned long, unsigned long, std::__1::function<void (unsigned long)>, itk::ProcessObject*)::$_2&, unsigned long&, unsigned long const&>(itk::PoolMultiThreader::ParallelizeArray(unsigned long, unsigned long, std::__1::function<void (unsigned long)>, itk::ProcessObject*)::$_2&, unsigned long&, unsigned long const&)::'lambda'()&>(itk::PoolMultiThreader::ParallelizeArray(unsigned long, unsigned long, std::__1::function<void (unsigned long)>, itk::ProcessObject*)::$_2&, unsigned long&, unsigned long const&) invoke.h:340 (ITKLevelSetsTestDriver:arm64+0x10123dfe0)
    #12 std::__1::__packaged_task_func<std::__1::future<std::__1::invoke_result<itk::PoolMultiThreader::ParallelizeArray(unsigned long, unsigned long, std::__1::function<void (unsigned long)>, itk::ProcessObject*)::$_2&, unsigned long&, unsigned long const&>::type> itk::ThreadPool::AddWork<itk::PoolMultiThreader::ParallelizeArray(unsigned long, unsigned long, std::__1::function<void (unsigned long)>, itk::ProcessObject*)::$_2&, unsigned long&, unsigned long const&>(itk::PoolMultiThreader::ParallelizeArray(unsigned long, unsigned long, std::__1::function<void (unsigned long)>, itk::ProcessObject*)::$_2&, unsigned long&, unsigned long const&)::'lambda'(), std::__1::allocator<std::__1::future<std::__1::invoke_result<itk::PoolMultiThreader::ParallelizeArray(unsigned long, unsigned long, std::__1::function<void (unsigned long)>, itk::ProcessObject*)::$_2&, unsigned long&, unsigned long const&>::type> itk::ThreadPool::AddWork<itk::PoolMultiThreader::ParallelizeArray(unsigned long, unsigned long, std::__1::function<void (unsigned long)>, itk::ProcessObject*)::$_2&, unsigned long&, unsigned long const&>(itk::PoolMultiThreader::ParallelizeArray(unsigned long, unsigned long, std::__1::function<void (unsigned long)>, itk::ProcessObject*)::$_2&, unsigned long&, unsigned long const&)::'lambda'()>, void* ()>::operator()() future:1706 (ITKLevelSetsTestDriver:arm64+0x10123d71c)
    #13 std::__1::__packaged_task_function<void* ()>::operator()() const future:1892 (ITKLevelSetsTestDriver:arm64+0x10123b484)
    #14 std::__1::packaged_task<void* ()>::operator()() future:1969 (ITKLevelSetsTestDriver:arm64+0x10123b29c)
    #15 std::__1::future<std::__1::invoke_result<itk::PoolMultiThreader::ParallelizeArray(unsigned long, unsigned long, std::__1::function<void (unsigned long)>, itk::ProcessObject*)::$_2&, unsigned long&, unsigned long const&>::type> itk::ThreadPool::AddWork<itk::PoolMultiThreader::ParallelizeArray(unsigned long, unsigned long, std::__1::function<void (unsigned long)>, itk::ProcessObject*)::$_2&, unsigned long&, unsigned long const&>(itk::PoolMultiThreader::ParallelizeArray(unsigned long, unsigned long, std::__1::function<void (unsigned long)>, itk::ProcessObject*)::$_2&, unsigned long&, unsigned long const&)::'lambda0'()::operator()() const itkThreadPool.h:97 (ITKLevelSetsTestDriver:arm64+0x1012414ec)
    #16 decltype(std::declval<itk::PoolMultiThreader::ParallelizeArray(unsigned long, unsigned long, std::__1::function<void (unsigned long)>, itk::ProcessObject*)::$_2&>()(std::declval<unsigned long&>(), std::declval<unsigned long const&>())) std::__1::__invoke[abi:ue170006]<std::__1::future<std::__1::invoke_result<itk::PoolMultiThreader::ParallelizeArray(unsigned long, unsigned long, std::__1::function<void (unsigned long)>, itk::ProcessObject*)::$_2&, unsigned long&, unsigned long const&>::type> itk::ThreadPool::AddWork<itk::PoolMultiThreader::ParallelizeArray(unsigned long, unsigned long, std::__1::function<void (unsigned long)>, itk::ProcessObject*)::$_2&, unsigned long&, unsigned long const&>(itk::PoolMultiThreader::ParallelizeArray(unsigned long, unsigned long, std::__1::function<void (unsigned long)>, itk::ProcessObject*)::$_2&, unsigned long&, unsigned long const&)::'lambda0'()&>(itk::PoolMultiThreader::ParallelizeArray(unsigned long, unsigned long, std::__1::function<void (unsigned long)>, itk::ProcessObject*)::$_2&, unsigned long&, unsigned long const&) invoke.h:340 (ITKLevelSetsTestDriver:arm64+0x101241494)
    #17 void std::__1::__invoke_void_return_wrapper<void, true>::__call[abi:ue170006]<std::__1::future<std::__1::invoke_result<itk::PoolMultiThreader::ParallelizeArray(unsigned long, unsigned long, std::__1::function<void (unsigned long)>, itk::ProcessObject*)::$_2&, unsigned long&, unsigned long const&>::type> itk::ThreadPool::AddWork<itk::PoolMultiThreader::ParallelizeArray(unsigned long, unsigned long, std::__1::function<void (unsigned long)>, itk::ProcessObject*)::$_2&, unsigned long&, unsigned long const&>(itk::PoolMultiThreader::ParallelizeArray(unsigned long, unsigned long, std::__1::function<void (unsigned long)>, itk::ProcessObject*)::$_2&, unsigned long&, unsigned long const&)::'lambda0'()&>(std::__1::future<std::__1::invoke_result<itk::PoolMultiThreader::ParallelizeArray(unsigned long, unsigned long, std::__1::function<void (unsigned long)>, itk::ProcessObject*)::$_2&, unsigned long&, unsigned long const&>::type> itk::ThreadPool::AddWork<itk::PoolMultiThreader::ParallelizeArray(unsigned long, unsigned long, std::__1::function<void (unsigned long)>, itk::ProcessObject*)::$_2&, unsigned long&, unsigned long const&>(itk::PoolMultiThreader::ParallelizeArray(unsigned long, unsigned long, std::__1::function<void (unsigned long)>, itk::ProcessObject*)::$_2&, unsigned long&, unsigned long const&)::'lambda0'()&) invoke.h:415 (ITKLevelSetsTestDriver:arm64+0x1012413f8)
    #18 std::__1::__function::__alloc_func<std::__1::future<std::__1::invoke_result<itk::PoolMultiThreader::ParallelizeArray(unsigned long, unsigned long, std::__1::function<void (unsigned long)>, itk::ProcessObject*)::$_2&, unsigned long&, unsigned long const&>::type> itk::ThreadPool::AddWork<itk::PoolMultiThreader::ParallelizeArray(unsigned long, unsigned long, std::__1::function<void (unsigned long)>, itk::ProcessObject*)::$_2&, unsigned long&, unsigned long const&>(itk::PoolMultiThreader::ParallelizeArray(unsigned long, unsigned long, std::__1::function<void (unsigned long)>, itk::ProcessObject*)::$_2&, unsigned long&, unsigned long const&)::'lambda0'(), std::__1::allocator<std::__1::future<std::__1::invoke_result<itk::PoolMultiThreader::ParallelizeArray(unsigned long, unsigned long, std::__1::function<void (unsigned long)>, itk::ProcessObject*)::$_2&, unsigned long&, unsigned long const&>::type> itk::ThreadPool::AddWork<itk::PoolMultiThreader::ParallelizeArray(unsigned long, unsigned long, std::__1::function<void (unsigned long)>, itk::ProcessObject*)::$_2&, unsigned long&, unsigned long const&>(itk::PoolMultiThreader::ParallelizeArray(unsigned long, unsigned long, std::__1::function<void (unsigned long)>, itk::ProcessObject*)::$_2&, unsigned long&, unsigned long const&)::'lambda0'()>, void ()>::operator()[abi:ue170006]() function.h:193 (ITKLevelSetsTestDriver:arm64+0x1012413a4)
    #19 std::__1::__function::__func<std::__1::future<std::__1::invoke_result<itk::PoolMultiThreader::ParallelizeArray(unsigned long, unsigned long, std::__1::function<void (unsigned long)>, itk::ProcessObject*)::$_2&, unsigned long&, unsigned long const&>::type> itk::ThreadPool::AddWork<itk::PoolMultiThreader::ParallelizeArray(unsigned long, unsigned long, std::__1::function<void (unsigned long)>, itk::ProcessObject*)::$_2&, unsigned long&, unsigned long const&>(itk::PoolMultiThreader::ParallelizeArray(unsigned long, unsigned long, std::__1::function<void (unsigned long)>, itk::ProcessObject*)::$_2&, unsigned long&, unsigned long const&)::'lambda0'(), std::__1::allocator<std::__1::future<std::__1::invoke_result<itk::PoolMultiThreader::ParallelizeArray(unsigned long, unsigned long, std::__1::function<void (unsigned long)>, itk::ProcessObject*)::$_2&, unsigned long&, unsigned long const&>::type> itk::ThreadPool::AddWork<itk::PoolMultiThreader::ParallelizeArray(unsigned long, unsigned long, std::__1::function<void (unsigned long)>, itk::ProcessObject*)::$_2&, unsigned long&, unsigned long const&>(itk::PoolMultiThreader::ParallelizeArray(unsigned long, unsigned long, std::__1::function<void (unsigned long)>, itk::ProcessObject*)::$_2&, unsigned long&, unsigned long const&)::'lambda0'()>, void ()>::operator()() function.h:364 (ITKLevelSetsTestDriver:arm64+0x10123f008)
    #20 std::__1::__function::__value_func<void ()>::operator()[abi:ue170006]() const function.h:518 (ITKLevelSetsTestDriver:arm64+0x10120924c)
    #21 std::__1::function<void ()>::operator()() const function.h:1169 (ITKLevelSetsTestDriver:arm64+0x101204a88)
    #22 itk::ThreadPool::ThreadExecute() itkThreadPool.cxx:211 (ITKLevelSetsTestDriver:arm64+0x10124954c)
    #23 decltype(std::declval<void (*)()>()()) std::__1::__invoke[abi:ue170006]<void (*)()>(void (*&&)()) invoke.h:340 (ITKLevelSetsTestDriver:arm64+0x101252dc8)
    #24 void std::__1::__thread_execute[abi:ue170006]<std::__1::unique_ptr<std::__1::__thread_struct, std::__1::default_delete<std::__1::__thread_struct>>, void (*)()>(std::__1::tuple<std::__1::unique_ptr<std::__1::__thread_struct, std::__1::default_delete<std::__1::__thread_struct>>, void (*)()>&, std::__1::__tuple_indices<>) thread.h:227 (ITKLevelSetsTestDriver:arm64+0x101252ce4)
    #25 void* std::__1::__thread_proxy[abi:ue170006]<std::__1::tuple<std::__1::unique_ptr<std::__1::__thread_struct, std::__1::default_delete<std::__1::__thread_struct>>, void (*)()>>(void*) thread.h:238 (ITKLevelSetsTestDriver:arm64+0x101252078)

  Previous write of size 1 at 0x00010b524516 by thread T24:
    #0 itk::Image<signed char, 3u>::SetPixel(itk::Index<3u> const&, signed char const&) itkImage.h:211 (ITKLevelSetsTestDriver:arm64+0x100098194)
    #1 itk::ParallelSparseFieldLevelSetImageFilter<itk::Image<float, 3u>, itk::Image<float, 3u>>::ThreadedUpdateActiveLayerValues(double const&, itk::SparseFieldLayer<itk::ParallelSparseFieldLevelSetNode<itk::Index<3u>>>*, itk::SparseFieldLayer<itk::ParallelSparseFieldLevelSetNode<itk::Index<3u>>>*, unsigned int) itkParallelSparseFieldLevelSetImageFilter.hxx:1583 (ITKLevelSetsTestDriver:arm64+0x1002f9818)
    #2 itk::ParallelSparseFieldLevelSetImageFilter<itk::Image<float, 3u>, itk::Image<float, 3u>>::ThreadedApplyUpdate(double const&, unsigned int) itkParallelSparseFieldLevelSetImageFilter.hxx:1374 (ITKLevelSetsTestDriver:arm64+0x1002c80fc)
    #3 itk::ParallelSparseFieldLevelSetImageFilter<itk::Image<float, 3u>, itk::Image<float, 3u>>::Iterate()::'lambda1'(unsigned long)::operator()(unsigned long) const itkParallelSparseFieldLevelSetImageFilter.hxx:1210 (ITKLevelSetsTestDriver:arm64+0x1002eb744)
    #4 decltype(std::declval<itk::ParallelSparseFieldLevelSetImageFilter<itk::Image<float, 3u>, itk::Image<float, 3u>>::Iterate()::'lambda1'(unsigned long)&>()(std::declval<unsigned long>())) std::__1::__invoke[abi:ue170006]<itk::ParallelSparseFieldLevelSetImageFilter<itk::Image<float, 3u>, itk::Image<float, 3u>>::Iterate()::'lambda1'(unsigned long)&, unsigned long>(itk::ParallelSparseFieldLevelSetImageFilter<itk::Image<float, 3u>, itk::Image<float, 3u>>::Iterate()::'lambda1'(unsigned long)&, unsigned long&&) invoke.h:340 (ITKLevelSetsTestDriver:arm64+0x1002eb6ac)
    #5 void std::__1::__invoke_void_return_wrapper<void, true>::__call[abi:ue170006]<itk::ParallelSparseFieldLevelSetImageFilter<itk::Image<float, 3u>, itk::Image<float, 3u>>::Iterate()::'lambda1'(unsigned long)&, unsigned long>(itk::ParallelSparseFieldLevelSetImageFilter<itk::Image<float, 3u>, itk::Image<float, 3u>>::Iterate()::'lambda1'(unsigned long)&, unsigned long&&) invoke.h:415 (ITKLevelSetsTestDriver:arm64+0x1002eb5e8)
    #6 std::__1::__function::__alloc_func<itk::ParallelSparseFieldLevelSetImageFilter<itk::Image<float, 3u>, itk::Image<float, 3u>>::Iterate()::'lambda1'(unsigned long), std::__1::allocator<itk::ParallelSparseFieldLevelSetImageFilter<itk::Image<float, 3u>, itk::Image<float, 3u>>::Iterate()::'lambda1'(unsigned long)>, void (unsigned long)>::operator()[abi:ue170006](unsigned long&&) function.h:193 (ITKLevelSetsTestDriver:arm64+0x1002eb584)
    #7 std::__1::__function::__func<itk::ParallelSparseFieldLevelSetImageFilter<itk::Image<float, 3u>, itk::Image<float, 3u>>::Iterate()::'lambda1'(unsigned long), std::__1::allocator<itk::ParallelSparseFieldLevelSetImageFilter<itk::Image<float, 3u>, itk::Image<float, 3u>>::Iterate()::'lambda1'(unsigned long)>, void (unsigned long)>::operator()(unsigned long&&) function.h:364 (ITKLevelSetsTestDriver:arm64+0x1002e94bc)
    #8 std::__1::__function::__value_func<void (unsigned long)>::operator()[abi:ue170006](unsigned long&&) const function.h:518 (ITKLevelSetsTestDriver:arm64+0x1011cad4c)
    #9 std::__1::function<void (unsigned long)>::operator()(unsigned long) const function.h:1169 (ITKLevelSetsTestDriver:arm64+0x1011c5e10)
    #10 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 (ITKLevelSetsTestDriver:arm64+0x10123e10c)
    #11 std::__1::future<std::__1::invoke_result<itk::PoolMultiThreader::ParallelizeArray(unsigned long, unsigned long, std::__1::function<void (unsigned long)>, itk::ProcessObject*)::$_2&, unsigned long&, unsigned long const&>::type> itk::ThreadPool::AddWork<itk::PoolMultiThreader::ParallelizeArray(unsigned long, unsigned long, std::__1::function<void (unsigned long)>, itk::ProcessObject*)::$_2&, unsigned long&, unsigned long const&>(itk::PoolMultiThreader::ParallelizeArray(unsigned long, unsigned long, std::__1::function<void (unsigned long)>, itk::ProcessObject*)::$_2&, unsigned long&, unsigned long const&)::'lambda'()::operator()() const itkThreadPool.h:92 (ITKLevelSetsTestDriver:arm64+0x10123e068)
    #12 decltype(std::declval<itk::PoolMultiThreader::ParallelizeArray(unsigned long, unsigned long, std::__1::function<void (unsigned long)>, itk::ProcessObject*)::$_2&>()(std::declval<unsigned long&>(), std::declval<unsigned long const&>())) std::__1::__invoke[abi:ue170006]<std::__1::future<std::__1::invoke_result<itk::PoolMultiThreader::ParallelizeArray(unsigned long, unsigned long, std::__1::function<void (unsigned long)>, itk::ProcessObject*)::$_2&, unsigned long&, unsigned long const&>::type> itk::ThreadPool::AddWork<itk::PoolMultiThreader::ParallelizeArray(unsigned long, unsigned long, std::__1::function<void (unsigned long)>, itk::ProcessObject*)::$_2&, unsigned long&, unsigned long const&>(itk::PoolMultiThreader::ParallelizeArray(unsigned long, unsigned long, std::__1::function<void (unsigned long)>, itk::ProcessObject*)::$_2&, unsigned long&, unsigned long const&)::'lambda'()&>(itk::PoolMultiThreader::ParallelizeArray(unsigned long, unsigned long, std::__1::function<void (unsigned long)>, itk::ProcessObject*)::$_2&, unsigned long&, unsigned long const&) invoke.h:340 (ITKLevelSetsTestDriver:arm64+0x10123dfe0)
    #13 std::__1::__packaged_task_func<std::__1::future<std::__1::invoke_result<itk::PoolMultiThreader::ParallelizeArray(unsigned long, unsigned long, std::__1::function<void (unsigned long)>, itk::ProcessObject*)::$_2&, unsigned long&, unsigned long const&>::type> itk::ThreadPool::AddWork<itk::PoolMultiThreader::ParallelizeArray(unsigned long, unsigned long, std::__1::function<void (unsigned long)>, itk::ProcessObject*)::$_2&, unsigned long&, unsigned long const&>(itk::PoolMultiThreader::ParallelizeArray(unsigned long, unsigned long, std::__1::function<void (unsigned long)>, itk::ProcessObject*)::$_2&, unsigned long&, unsigned long const&)::'lambda'(), std::__1::allocator<std::__1::future<std::__1::invoke_result<itk::PoolMultiThreader::ParallelizeArray(unsigned long, unsigned long, std::__1::function<void (unsigned long)>, itk::ProcessObject*)::$_2&, unsigned long&, unsigned long const&>::type> itk::ThreadPool::AddWork<itk::PoolMultiThreader::ParallelizeArray(unsigned long, unsigned long, std::__1::function<void (unsigned long)>, itk::ProcessObject*)::$_2&, unsigned long&, unsigned long const&>(itk::PoolMultiThreader::ParallelizeArray(unsigned long, unsigned long, std::__1::function<void (unsigned long)>, itk::ProcessObject*)::$_2&, unsigned long&, unsigned long const&)::'lambda'()>, void* ()>::operator()() future:1706 (ITKLevelSetsTestDriver:arm64+0x10123d71c)
    #14 std::__1::__packaged_task_function<void* ()>::operator()() const future:1892 (ITKLevelSetsTestDriver:arm64+0x10123b484)
    #15 std::__1::packaged_task<void* ()>::operator()() future:1969 (ITKLevelSetsTestDriver:arm64+0x10123b29c)
    #16 std::__1::future<std::__1::invoke_result<itk::PoolMultiThreader::ParallelizeArray(unsigned long, unsigned long, std::__1::function<void (unsigned long)>, itk::ProcessObject*)::$_2&, unsigned long&, unsigned long const&>::type> itk::ThreadPool::AddWork<itk::PoolMultiThreader::ParallelizeArray(unsigned long, unsigned long, std::__1::function<void (unsigned long)>, itk::ProcessObject*)::$_2&, unsigned long&, unsigned long const&>(itk::PoolMultiThreader::ParallelizeArray(unsigned long, unsigned long, std::__1::function<void (unsigned long)>, itk::ProcessObject*)::$_2&, unsigned long&, unsigned long const&)::'lambda0'()::operator()() const itkThreadPool.h:97 (ITKLevelSetsTestDriver:arm64+0x1012414ec)
    #17 decltype(std::declval<itk::PoolMultiThreader::ParallelizeArray(unsigned long, unsigned long, std::__1::function<void (unsigned long)>, itk::ProcessObject*)::$_2&>()(std::declval<unsigned long&>(), std::declval<unsigned long const&>())) std::__1::__invoke[abi:ue170006]<std::__1::future<std::__1::invoke_result<itk::PoolMultiThreader::ParallelizeArray(unsigned long, unsigned long, std::__1::function<void (unsigned long)>, itk::ProcessObject*)::$_2&, unsigned long&, unsigned long const&>::type> itk::ThreadPool::AddWork<itk::PoolMultiThreader::ParallelizeArray(unsigned long, unsigned long, std::__1::function<void (unsigned long)>, itk::ProcessObject*)::$_2&, unsigned long&, unsigned long const&>(itk::PoolMultiThreader::ParallelizeArray(unsigned long, unsigned long, std::__1::function<void (unsigned long)>, itk::ProcessObject*)::$_2&, unsigned long&, unsigned long const&)::'lambda0'()&>(itk::PoolMultiThreader::ParallelizeArray(unsigned long, unsigned long, std::__1::function<void (unsigned long)>, itk::ProcessObject*)::$_2&, unsigned long&, unsigned long const&) invoke.h:340 (ITKLevelSetsTestDriver:arm64+0x101241494)
    #18 void std::__1::__invoke_void_return_wrapper<void, true>::__call[abi:ue170006]<std::__1::future<std::__1::invoke_result<itk::PoolMultiThreader::ParallelizeArray(unsigned long, unsigned long, std::__1::function<void (unsigned long)>, itk::ProcessObject*)::$_2&, unsigned long&, unsigned long const&>::type> itk::ThreadPool::AddWork<itk::PoolMultiThreader::ParallelizeArray(unsigned long, unsigned long, std::__1::function<void (unsigned long)>, itk::ProcessObject*)::$_2&, unsigned long&, unsigned long const&>(itk::PoolMultiThreader::ParallelizeArray(unsigned long, unsigned long, std::__1::function<void (unsigned long)>, itk::ProcessObject*)::$_2&, unsigned long&, unsigned long const&)::'lambda0'()&>(std::__1::future<std::__1::invoke_result<itk::PoolMultiThreader::ParallelizeArray(unsigned long, unsigned long, std::__1::function<void (unsigned long)>, itk::ProcessObject*)::$_2&, unsigned long&, unsigned long const&>::type> itk::ThreadPool::AddWork<itk::PoolMultiThreader::ParallelizeArray(unsigned long, unsigned long, std::__1::function<void (unsigned long)>, itk::ProcessObject*)::$_2&, unsigned long&, unsigned long const&>(itk::PoolMultiThreader::ParallelizeArray(unsigned long, unsigned long, std::__1::function<void (unsigned long)>, itk::ProcessObject*)::$_2&, unsigned long&, unsigned long const&)::'lambda0'()&) invoke.h:415 (ITKLevelSetsTestDriver:arm64+0x1012413f8)
    #19 std::__1::__function::__alloc_func<std::__1::future<std::__1::invoke_result<itk::PoolMultiThreader::ParallelizeArray(unsigned long, unsigned long, std::__1::function<void (unsigned long)>, itk::ProcessObject*)::$_2&, unsigned long&, unsigned long const&>::type> itk::ThreadPool::AddWork<itk::PoolMultiThreader::ParallelizeArray(unsigned long, unsigned long, std::__1::function<void (unsigned long)>, itk::ProcessObject*)::$_2&, unsigned long&, unsigned long const&>(itk::PoolMultiThreader::ParallelizeArray(unsigned long, unsigned long, std::__1::function<void (unsigned long)>, itk::ProcessObject*)::$_2&, unsigned long&, unsigned long const&)::'lambda0'(), std::__1::allocator<std::__1::future<std::__1::invoke_result<itk::PoolMultiThreader::ParallelizeArray(unsigned long, unsigned long, std::__1::function<void (unsigned long)>, itk::ProcessObject*)::$_2&, unsigned long&, unsigned long const&>::type> itk::ThreadPool::AddWork<itk::PoolMultiThreader::ParallelizeArray(unsigned long, unsigned long, std::__1::function<void (unsigned long)>, itk::ProcessObject*)::$_2&, unsigned long&, unsigned long const&>(itk::PoolMultiThreader::ParallelizeArray(unsigned long, unsigned long, std::__1::function<void (unsigned long)>, itk::ProcessObject*)::$_2&, unsigned long&, unsigned long const&)::'lambda0'()>, void ()>::operator()[abi:ue170006]() function.h:193 (ITKLevelSetsTestDriver:arm64+0x1012413a4)
    #20 std::__1::__function::__func<std::__1::future<std::__1::invoke_result<itk::PoolMultiThreader::ParallelizeArray(unsigned long, unsigned long, std::__1::function<void (unsigned long)>, itk::ProcessObject*)::$_2&, unsigned long&, unsigned long const&>::type> itk::ThreadPool::AddWork<itk::PoolMultiThreader::ParallelizeArray(unsigned long, unsigned long, std::__1::function<void (unsigned long)>, itk::ProcessObject*)::$_2&, unsigned long&, unsigned long const&>(itk::PoolMultiThreader::ParallelizeArray(unsigned long, unsigned long, std::__1::function<void (unsigned long)>, itk::ProcessObject*)::$_2&, unsigned long&, unsigned long const&)::'lambda0'(), std::__1::allocator<std::__1::future<std::__1::invoke_result<itk::PoolMultiThreader::ParallelizeArray(unsigned long, unsigned long, std::__1::function<void (unsigned long)>, itk::ProcessObject*)::$_2&, unsigned long&, unsigned long const&>::type> itk::ThreadPool::AddWork<itk::PoolMultiThreader::ParallelizeArray(unsigned long, unsigned long, std::__1::function<void (unsigned long)>, itk::ProcessObject*)::$_2&, unsigned long&, unsigned long const&>(itk::PoolMultiThreader::ParallelizeArray(unsigned long, unsigned long, std::__1::function<void (unsigned long)>, itk::ProcessObject*)::$_2&, unsigned long&, unsigned long const&)::'lambda0'()>, void ()>::operator()() function.h:364 (ITKLevelSetsTestDriver:arm64+0x10123f008)
    #21 std::__1::__function::__value_func<void ()>::operator()[abi:ue170006]() const function.h:518 (ITKLevelSetsTestDriver:arm64+0x10120924c)
    #22 std::__1::function<void ()>::operator()() const function.h:1169 (ITKLevelSetsTestDriver:arm64+0x101204a88)
    #23 itk::ThreadPool::ThreadExecute() itkThreadPool.cxx:211 (ITKLevelSetsTestDriver:arm64+0x10124954c)
    #24 decltype(std::declval<void (*)()>()()) std::__1::__invoke[abi:ue170006]<void (*)()>(void (*&&)()) invoke.h:340 (ITKLevelSetsTestDriver:arm64+0x101252dc8)
    #25 void std::__1::__thread_execute[abi:ue170006]<std::__1::unique_ptr<std::__1::__thread_struct, std::__1::default_delete<std::__1::__thread_struct>>, void (*)()>(std::__1::tuple<std::__1::unique_ptr<std::__1::__thread_struct, std::__1::default_delete<std::__1::__thread_struct>>, void (*)()>&, std::__1::__tuple_indices<>) thread.h:227 (ITKLevelSetsTestDriver:arm64+0x101252ce4)
    #26 void* std::__1::__thread_proxy[abi:ue170006]<std::__1::tuple<std::__1::unique_ptr<std::__1::__thread_struct, std::__1::default_delete<std::__1::__thread_struct>>, void (*)()>>(void*) thread.h:238 (ITKLevelSetsTestDriver:arm64+0x101252078)

  Location is heap block of size 262144 at 0x00010b504000 allocated by main thread:
    #0 operator new[](unsigned long) <null>:69111428 (libclang_rt.tsan_osx_dynamic.dylib:arm64e+0x8451c)
    #1 itk::ImportImageContainer<unsigned long, signed char>::AllocateElements(unsigned long, bool) const itkImportImageContainer.hxx:168 (ITKLevelSetsTestDriver:arm64+0x1000a20b4)
    #2 itk::ImportImageContainer<unsigned long, signed char>::Reserve(unsigned long, bool) itkImportImageContainer.hxx:76 (ITKLevelSetsTestDriver:arm64+0x1000a2ca0)
    #3 itk::Image<signed char, 3u>::Allocate(bool) itkImage.hxx:46 (ITKLevelSetsTestDriver:arm64+0x1000a1218)
    #4 itk::ParallelSparseFieldLevelSetImageFilter<itk::Image<float, 3u>, itk::Image<float, 3u>>::Iterate() itkParallelSparseFieldLevelSetImageFilter.hxx:1092 (ITKLevelSetsTestDriver:arm64+0x1002d2078)
    #5 itk::ParallelSparseFieldLevelSetImageFilter<itk::Image<float, 3u>, itk::Image<float, 3u>>::GenerateData() itkParallelSparseFieldLevelSetImageFilter.hxx:161 (ITKLevelSetsTestDriver:arm64+0x1002c6364)
    #6 itk::ProcessObject::UpdateOutputData(itk::DataObject*) itkProcessObject.cxx:1694 (ITKLevelSetsTestDriver:arm64+0x1011ae638)
    #7 itk::DataObject::UpdateOutputData() itkDataObject.cxx:388 (ITKLevelSetsTestDriver:arm64+0x1011eb4b4)
    #8 itk::ImageBase<3u>::UpdateOutputData() itkImageBase.hxx:265 (ITKLevelSetsTestDriver:arm64+0x100010f80)
    #9 itk::DataObject::Update() itkDataObject.cxx:319 (ITKLevelSetsTestDriver:arm64+0x1011eafc8)
    #10 itk::ProcessObject::Update() itkProcessObject.cxx:1286 (ITKLevelSetsTestDriver:arm64+0x1011ad028)
    #11 itkParallelSparseFieldLevelSetImageFilterTest(int, char**) itkParallelSparseFieldLevelSetImageFilterTest.cxx:316 (ITKLevelSetsTestDriver:arm64+0x1002c3498)
    #12 main ITKLevelSetsTestDriver.cxx:328 (ITKLevelSetsTestDriver:arm64+0x100003668)

  Thread T19 (tid=32858443, running) created by main thread at:
    #0 pthread_create <null>:69111428 (libclang_rt.tsan_osx_dynamic.dylib:arm64e+0x3062c)
    #1 std::__1::__libcpp_thread_create[abi:ue170006](_opaque_pthread_t**, void* (*)(void*), void*) __threading_support:371 (ITKLevelSetsTestDriver:arm64+0x101251fe0)
    #2 std::__1::thread::thread<void (*)(), void>(void (*&&)()) thread.h:254 (ITKLevelSetsTestDriver:arm64+0x101251d84)
    #3 std::__1::thread::thread<void (*)(), void>(void (*&&)()) thread.h:246 (ITKLevelSetsTestDriver:arm64+0x101251cac)
    #4 void std::__1::allocator<std::__1::thread>::construct[abi:ue170006]<std::__1::thread, void (*)()>(std::__1::thread*, void (*&&)()) allocator.h:167 (ITKLevelSetsTestDriver:arm64+0x101251c44)
    #5 void std::__1::allocator_traits<std::__1::allocator<std::__1::thread>>::construct[abi:ue170006]<std::__1::thread, void (*)(), void>(std::__1::allocator<std::__1::thread>&, std::__1::thread*, void (*&&)()) allocator_traits.h:296 (ITKLevelSetsTestDriver:arm64+0x101251aa0)
    #6 void std::__1::vector<std::__1::thread, std::__1::allocator<std::__1::thread>>::__construct_one_at_end[abi:ue170006]<void (*)()>(void (*&&)()) vector:919 (ITKLevelSetsTestDriver:arm64+0x1012517ec)
    #7 std::__1::thread& std::__1::vector<std::__1::thread, std::__1::allocator<std::__1::thread>>::emplace_back<void (*)()>(void (*&&)()) vector:1678 (ITKLevelSetsTestDriver:arm64+0x1012493a4)
    #8 itk::ThreadPool::ThreadPool() itkThreadPool.cxx:118 (ITKLevelSetsTestDriver:arm64+0x101249004)
    #9 itk::ThreadPool::ThreadPool() itkThreadPool.cxx:108 (ITKLevelSetsTestDriver:arm64+0x101249694)
    #10 itk::ThreadPool::GetInstance()::$_1::operator()() const itkThreadPool.cxx:83 (ITKLevelSetsTestDriver:arm64+0x10124ecbc)
    #11 decltype(std::declval<itk::ThreadPool::GetInstance()::$_1>()()) std::__1::__invoke[abi:ue170006]<itk::ThreadPool::GetInstance()::$_1>(itk::ThreadPool::GetInstance()::$_1&&) invoke.h:340 (ITKLevelSetsTestDriver:arm64+0x10124ebac)
    #12 void std::__1::__call_once_param<std::__1::tuple<itk::ThreadPool::GetInstance()::$_1&&>>::__execute[abi:ue170006]<>(std::__1::__tuple_indices<>) mutex:632 (ITKLevelSetsTestDriver:arm64+0x10124eb58)
    #13 std::__1::__call_once_param<std::__1::tuple<itk::ThreadPool::GetInstance()::$_1&&>>::operator()[abi:ue170006]() mutex:624 (ITKLevelSetsTestDriver:arm64+0x10124eaf0)
    #14 void std::__1::__call_once_proxy[abi:ue170006]<std::__1::tuple<itk::ThreadPool::GetInstance()::$_1&&>>(void*) mutex:660 (ITKLevelSetsTestDriver:arm64+0x10124e8c4)
    #15 __tsan::(anonymous namespace)::call_once_callback_wrapper(void*) <null>:69111428 (libclang_rt.tsan_osx_dynamic.dylib:arm64e+0x7e250)
    #16 itk::ThreadPool::GetInstance() itkThreadPool.cxx:79 (ITKLevelSetsTestDriver:arm64+0x101248aa4)
    #17 itk::PoolMultiThreader::PoolMultiThreader() itkPoolMultiThreader.cxx:81 (ITKLevelSetsTestDriver:arm64+0x10122bcb0)
    #18 itk::PoolMultiThreader::PoolMultiThreader() itkPoolMultiThreader.cxx:82 (ITKLevelSetsTestDriver:arm64+0x10122c178)
    #19 itk::PoolMultiThreader::New() itkPoolMultiThreader.h:57 (ITKLevelSetsTestDriver:arm64+0x1011c51c4)
    #20 itk::MultiThreaderBase::New() itkMultiThreaderBase.cxx:389 (ITKLevelSetsTestDriver:arm64+0x1011c4aac)
    #21 itk::ProcessObject::ProcessObject() itkProcessObject.cxx:72 (ITKLevelSetsTestDriver:arm64+0x1011a2fc0)
    #22 itk::ImageFileWriter<itk::Image<float, 3u>>::ImageFileWriter() itkImageFileWriter.h:217 (ITKLevelSetsTestDriver:arm64+0x1002fc088)
    #23 itk::ImageFileWriter<itk::Image<float, 3u>>::ImageFileWriter() itkImageFileWriter.h:217 (ITKLevelSetsTestDriver:arm64+0x1002fbf18)
    #24 itk::ImageFileWriter<itk::Image<float, 3u>>::New() itkImageFileWriter.h:101 (ITKLevelSetsTestDriver:arm64+0x1002c3a54)
    #25 itkParallelSparseFieldLevelSetImageFilterTest(int, char**) itkParallelSparseFieldLevelSetImageFilterTest.cxx:281 (ITKLevelSetsTestDriver:arm64+0x1002c2d34)
    #26 main ITKLevelSetsTestDriver.cxx:328 (ITKLevelSetsTestDriver:arm64+0x100003668)

  Thread T24 (tid=32858448, running) created by main thread at:
    #0 pthread_create <null>:69111428 (libclang_rt.tsan_osx_dynamic.dylib:arm64e+0x3062c)
    #1 std::__1::__libcpp_thread_create[abi:ue170006](_opaque_pthread_t**, void* (*)(void*), void*) __threading_support:371 (ITKLevelSetsTestDriver:arm64+0x101251fe0)
    #2 std::__1::thread::thread<void (*)(), void>(void (*&&)()) thread.h:254 (ITKLevelSetsTestDriver:arm64+0x101251d84)
    #3 std::__1::thread::thread<void (*)(), void>(void (*&&)()) thread.h:246 (ITKLevelSetsTestDriver:arm64+0x101251cac)
    #4 void std::__1::allocator<std::__1::thread>::construct[abi:ue170006]<std::__1::thread, void (*)()>(std::__1::thread*, void (*&&)()) allocator.h:167 (ITKLevelSetsTestDriver:arm64+0x101251c44)
    #5 void std::__1::allocator_traits<std::__1::allocator<std::__1::thread>>::construct[abi:ue170006]<std::__1::thread, void (*)(), void>(std::__1::allocator<std::__1::thread>&, std::__1::thread*, void (*&&)()) allocator_traits.h:296 (ITKLevelSetsTestDriver:arm64+0x101251aa0)
    #6 void std::__1::vector<std::__1::thread, std::__1::allocator<std::__1::thread>>::__construct_one_at_end[abi:ue170006]<void (*)()>(void (*&&)()) vector:919 (ITKLevelSetsTestDriver:arm64+0x1012517ec)
    #7 std::__1::thread& std::__1::vector<std::__1::thread, std::__1::allocator<std::__1::thread>>::emplace_back<void (*)()>(void (*&&)()) vector:1678 (ITKLevelSetsTestDriver:arm64+0x1012493a4)
    #8 itk::ThreadPool::ThreadPool() itkThreadPool.cxx:118 (ITKLevelSetsTestDriver:arm64+0x101249004)
    #9 itk::ThreadPool::ThreadPool() itkThreadPool.cxx:108 (ITKLevelSetsTestDriver:arm64+0x101249694)
    #10 itk::ThreadPool::GetInstance()::$_1::operator()() const itkThreadPool.cxx:83 (ITKLevelSetsTestDriver:arm64+0x10124ecbc)
    #11 decltype(std::declval<itk::ThreadPool::GetInstance()::$_1>()()) std::__1::__invoke[abi:ue170006]<itk::ThreadPool::GetInstance()::$_1>(itk::ThreadPool::GetInstance()::$_1&&) invoke.h:340 (ITKLevelSetsTestDriver:arm64+0x10124ebac)
    #12 void std::__1::__call_once_param<std::__1::tuple<itk::ThreadPool::GetInstance()::$_1&&>>::__execute[abi:ue170006]<>(std::__1::__tuple_indices<>) mutex:632 (ITKLevelSetsTestDriver:arm64+0x10124eb58)
    #13 std::__1::__call_once_param<std::__1::tuple<itk::ThreadPool::GetInstance()::$_1&&>>::operator()[abi:ue170006]() mutex:624 (ITKLevelSetsTestDriver:arm64+0x10124eaf0)
    #14 void std::__1::__call_once_proxy[abi:ue170006]<std::__1::tuple<itk::ThreadPool::GetInstance()::$_1&&>>(void*) mutex:660 (ITKLevelSetsTestDriver:arm64+0x10124e8c4)
    #15 __tsan::(anonymous namespace)::call_once_callback_wrapper(void*) <null>:69111428 (libclang_rt.tsan_osx_dynamic.dylib:arm64e+0x7e250)
    #16 itk::ThreadPool::GetInstance() itkThreadPool.cxx:79 (ITKLevelSetsTestDriver:arm64+0x101248aa4)
    #17 itk::PoolMultiThreader::PoolMultiThreader() itkPoolMultiThreader.cxx:81 (ITKLevelSetsTestDriver:arm64+0x10122bcb0)
    #18 itk::PoolMultiThreader::PoolMultiThreader() itkPoolMultiThreader.cxx:82 (ITKLevelSetsTestDriver:arm64+0x10122c178)
    #19 itk::PoolMultiThreader::New() itkPoolMultiThreader.h:57 (ITKLevelSetsTestDriver:arm64+0x1011c51c4)
    #20 itk::MultiThreaderBase::New() itkMultiThreaderBase.cxx:389 (ITKLevelSetsTestDriver:arm64+0x1011c4aac)
    #21 itk::ProcessObject::ProcessObject() itkProcessObject.cxx:72 (ITKLevelSetsTestDriver:arm64+0x1011a2fc0)
    #22 itk::ImageFileWriter<itk::Image<float, 3u>>::ImageFileWriter() itkImageFileWriter.h:217 (ITKLevelSetsTestDriver:arm64+0x1002fc088)
    #23 itk::ImageFileWriter<itk::Image<float, 3u>>::ImageFileWriter() itkImageFileWriter.h:217 (ITKLevelSetsTestDriver:arm64+0x1002fbf18)
    #24 itk::ImageFileWriter<itk::Image<float, 3u>>::New() itkImageFileWriter.h:101 (ITKLevelSetsTestDriver:arm64+0x1002c3a54)
    #25 itkParallelSparseFieldLevelSetImageFilterTest(int, char**) itkParallelSparseFieldLevelSetImageFilterTest.cxx:281 (ITKLevelSetsTestDriver:arm64+0x1002c2d34)
    #26 main ITKLevelSetsTestDriver.cxx:328 (ITKLevelSetsTestDriver:arm64+0x100003668)

SUMMARY: ThreadSanitizer: data race itkParallelSparseFieldLevelSetImageFilter.hxx:1556 in itk::ParallelSparseFieldLevelSetImageFilter<itk::Image<float, 3u>, itk::Image<float, 3u>>::ThreadedUpdateActiveLayerValues(double const&, itk::SparseFieldLayer<itk::ParallelSparseFieldLevelSetNode<itk::Index<3u>>>*, itk::SparseFieldLayer<itk::ParallelSparseFieldLevelSetNode<itk::Index<3u>>>*, unsigned int)
==================

@blowekamp
Copy link
Member Author

@seanm That is a different test that the itkErodeObjectMorphologyImageFilterTest one.

Not sue sure why this hasn't gotten any approval yet.

@seanm
Copy link
Contributor

seanm commented Nov 26, 2024

@seanm That is a different test that the itkErodeObjectMorphologyImageFilterTest one.

Oh, I see the mistake. #4969 says itkErodeObjectMorphologyImageFilterTest in the summary, but I wrote itkParallelSparseFieldLevelSetImageFilterTest in the body. Dang copy-paste!

itkErodeObjectMorphologyImageFilterTest indeed passes with TSan now!

I'll go fix the #4969 text...

@blowekamp
Copy link
Member Author

@dzenanz Please review. This a minimal change to address the race condition without changing the logic, only moving the pixel copying from the threaded method to the before threaded method.

This filter's existential crisis is not related to the PR.

Copy link
Contributor

@N-Dekker N-Dekker left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The while (!oRegIter.IsAtEnd()) loop that you moved into the body of BeforeThreadedGenerateData() looks inefficient to me. I wonder, why not using itk::ImageAlgorithm::Copy? Because it looks like it's just copying all pixels from input to output! It's also a pity that the task of copying pixels is no longer parallelized with your PR, because I believe it's an "embarrassingly parallelizable" task on its own.

I also find the way this filter makes use of "faces" rather suspicious, as I mentioned at #4969 (comment) But as you say "This a minimal change to address the race condition without changing the logic", and I like PRs that present a minimal change to address a problem. 😃 I do think it could solve the race condition, so I'll approve it anyway, as it is now. Thanks Bradley! 👍

@dzenanz
Copy link
Member

dzenanz commented Nov 26, 2024

Good as-is, would be better with Niels' suggestion.

@blowekamp blowekamp merged commit 2816ed3 into InsightSoftwareConsortium:master Nov 26, 2024
17 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
area:Filtering Issues affecting the Filtering module type:Bug Inconsistencies or issues which will cause an incorrect result under some or all circumstances
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants