vtkSMPThreadLocal - potential issue.

Hi Developers

I have used numerous times in the past, but something really weird is going on. I am beginning to think that it is the C++ that comes with VS2022.

I am basically just finding the min and max of a set of points. It is a part of a new Triangulate25D that at some point, I would like to make a contribution about. It operates on structured grids and sine the vtkThreshold only can output an unstructured grid, I incorporated some threasholding in the filter.

If I insert breakpoints just before the .Local() statements, it ends up giving weird results. It is like multiple threads share some local variables. I have 20 cores - could that be the reason?

template <typename TPoints, typename TScalarArray>
struct vtkTriangulate25D::EvaluatePointsFunctor
{
  using TLS = vtkSMPThreadLocal<std::array<double, 2>>;
  TLS TLMinMaxDepth;

  vtkTriangulate25D* Self;
  vtkDataSet* Input;
  TPoints* Points;
  TScalarArray* ScalarsArray;
  vtkIdType NumberOfPoints;
  double RangeLow;
  double RangeHigh;
  vtkNew<vtkUnsignedCharArray> ValidityArray;

  EvaluatePointsFunctor(
    vtkTriangulate25D* self, vtkDataSet* input, TPoints* points, TScalarArray* scalarsArray)
    : Self(self)
    , Input(input)
    , Points(points)
    , ScalarsArray(scalarsArray)
    , NumberOfPoints(input->GetNumberOfPoints())
    , RangeLow(0.0)
    , RangeHigh(0.0)
  {
    this->ValidityArray->SetNumberOfComponents(1);
    this->ValidityArray->SetNumberOfValues(this->NumberOfPoints);
    auto validityArray = vtk::DataArrayValueRange<1>(this->ValidityArray);
    std::fill(std::begin(validityArray), std::end(validityArray), 0);
    if (this->NumberOfPoints > 0)
    {
      // ensure that internal structures are initialized.
      this->Input->GetPoint(0);
    }
  }

  void Initialize()
  {
    std::array<double, 2>& minmax = this->TLMinMaxDepth.Local();
    minmax[0] = VTK_DOUBLE_MAX;
    minmax[1] = VTK_DOUBLE_MIN;
  }

  void operator()(vtkIdType begin, vtkIdType end)
  {
    const bool isFirst = false; // vtkSMPTools::GetSingleThread();

    const auto& scalars = vtk::DataArrayTupleRange(this->ScalarsArray);
    const auto& points = vtk::DataArrayTupleRange<3>(this->Points);
    auto& validity = vtk::DataArrayValueRange<1>(this->ValidityArray);

    vtkIdType checkAbortInterval = std::min((end - begin) / 10 + 1, (vtkIdType)1000);

    // TODO: API type of input and max
    double minDepth = VTK_DOUBLE_MAX;
    double maxDepth = VTK_DOUBLE_MIN;
    for (vtkIdType pointId = begin; pointId < end; ++pointId)
    {
      if (pointId % checkAbortInterval == 0)
      {
        if (isFirst)
        {
          this->Self->CheckAbort();
        }
        if (this->Self->GetAbortOutput())
        {
          break;
        }
      }

      int keepPoint = 0;
      keepPoint = this->Self->EvaluateComponents(scalars, pointId);
      validity[pointId] = keepPoint ? MarkPointValid : 0;
      if (keepPoint)
      {
        auto point = points[pointId];
        double depth = point[Self->TwoDimType];
        minDepth = std::min(minDepth, depth);
        maxDepth = std::max(maxDepth, depth);
      }

      if (isFirst)
      {
        this->Self->UpdateProgress(end * 1.0 / this->NumberOfPoints * 1.0 / 3);
      }
    }
    std::array<double, 2>& minmax = this->TLMinMaxDepth.Local();
    minmax[0] = minDepth;
    minmax[1] = maxDepth;
  }

  void Reduce()
  {
    using TLSIter = TLS::iterator;
    TLSIter end = this->TLMinMaxDepth.end();
    double minDepth = VTK_DOUBLE_MAX;
    double maxDepth = VTK_DOUBLE_MIN;
    for (TLSIter itr = this->TLMinMaxDepth.begin(); itr != end; ++itr)
    {
      std::array<double, 2>& minmax = *itr;
      minDepth = std::min(minDepth, minmax[0]);
      maxDepth = std::max(maxDepth, minmax[1]);
    }
    std::cout << "MinDepth: " << minDepth << std::endl;
    std::cout << "MaxDepth: " << maxDepth << std::endl;
    this->RangeLow = minDepth;
    this->RangeHigh = maxDepth;
  }
}