From c19605529975875acab04948e0d9a4c6be27ab63 Mon Sep 17 00:00:00 2001 From: Daniel Baston Date: Tue, 14 Feb 2023 13:35:40 -0500 Subject: [PATCH] PreparedPolygon: Use std::call_once for lazy initialization --- include/geos/geom/prep/PreparedPolygon.h | 10 ++++- src/geom/prep/PreparedPolygon.cpp | 27 +++++++----- tests/unit/geom/prep/PreparedGeometryTest.cpp | 41 +++++++++++++++++++ 3 files changed, 66 insertions(+), 12 deletions(-) diff --git a/include/geos/geom/prep/PreparedPolygon.h b/include/geos/geom/prep/PreparedPolygon.h index a2feee6c9..f8239f4c1 100644 --- a/include/geos/geom/prep/PreparedPolygon.h +++ b/include/geos/geom/prep/PreparedPolygon.h @@ -50,13 +50,19 @@ namespace prep { // geos::geom::prep */ class PreparedPolygon : public BasicPreparedGeometry { private: - bool isRectangle; mutable std::unique_ptr segIntFinder; mutable std::unique_ptr ptOnGeomLoc; mutable std::unique_ptr indexedPtOnGeomLoc; - mutable noding::SegmentString::ConstVect segStrings; mutable std::unique_ptr indexedDistance; + mutable noding::SegmentString::ConstVect segStrings; + + mutable std::once_flag segIntFinderFlag; + mutable std::once_flag ptOnGeomLocFlag; + mutable std::once_flag indexedPtOnGeomLocFlag; + mutable std::once_flag indexedDistanceFlag; + bool isRectangle; + protected: public: PreparedPolygon(const geom::Geometry* geom); diff --git a/src/geom/prep/PreparedPolygon.cpp b/src/geom/prep/PreparedPolygon.cpp index 332c4798e..271957ab7 100644 --- a/src/geom/prep/PreparedPolygon.cpp +++ b/src/geom/prep/PreparedPolygon.cpp @@ -58,10 +58,11 @@ noding::FastSegmentSetIntersectionFinder* PreparedPolygon:: getIntersectionFinder() const { - if(! segIntFinder) { + std::call_once(segIntFinderFlag, [this]() { noding::SegmentStringUtil::extractSegmentStrings(&getGeometry(), segStrings); - segIntFinder.reset(new noding::FastSegmentSetIntersectionFinder(&segStrings)); - } + segIntFinder = detail::make_unique(&segStrings); + }); + return segIntFinder.get(); } @@ -73,13 +74,18 @@ getPointLocator() const // instead of an IndexedPointInAreaLocator. There's a reasonable chance we will only use this locator // once (for example, if we get here through Geometry::intersects). So we create a simple locator for the // first usage and switch to an indexed locator when it is clear we're in a multiple-use scenario. - if(! ptOnGeomLoc) { - ptOnGeomLoc = detail::make_unique(&getGeometry()); + if(!ptOnGeomLoc) { + std::call_once(ptOnGeomLocFlag, [this]() { + ptOnGeomLoc = detail::make_unique(&getGeometry()); + }); + return ptOnGeomLoc.get(); - } else if (!indexedPtOnGeomLoc) { - indexedPtOnGeomLoc = detail::make_unique(getGeometry()); } + std::call_once(indexedPtOnGeomLocFlag, [this]() { + indexedPtOnGeomLoc = detail::make_unique(getGeometry()); + }); + return indexedPtOnGeomLoc.get(); } @@ -157,9 +163,10 @@ operation::distance::IndexedFacetDistance* PreparedPolygon:: getIndexedFacetDistance() const { - if(! indexedDistance ) { - indexedDistance.reset(new operation::distance::IndexedFacetDistance(&getGeometry())); - } + std::call_once(indexedDistanceFlag, [this]() { + indexedDistance = detail::make_unique(&getGeometry()); + }); + return indexedDistance.get(); } diff --git a/tests/unit/geom/prep/PreparedGeometryTest.cpp b/tests/unit/geom/prep/PreparedGeometryTest.cpp index efc5c4b82..ca9a4f949 100644 --- a/tests/unit/geom/prep/PreparedGeometryTest.cpp +++ b/tests/unit/geom/prep/PreparedGeometryTest.cpp @@ -12,6 +12,7 @@ #include // std #include +#include using namespace geos::geom; using geos::geom::prep::PreparedGeometry; @@ -68,4 +69,44 @@ void object::test<1> ensure( pg1->covers(g2.get())); } +// Check prepared geometry can be used from multiple threads +template<> +template<> +void object::test<2> +() +{ + std::vector> geoms; + std::vector> pgeoms; + std::vector threads; + + constexpr std::size_t nrow = 10; + constexpr std::size_t ncol = 10; + constexpr std::size_t nthreads = 10; + + for (std::size_t i = 0; i < ncol; i++) { + for (std::size_t j = 0; j < nrow; j++) { + CoordinateXY c(static_cast(i), static_cast(j)); + auto pt = factory->createPoint(c); + + geoms.emplace_back(pt->buffer(1.5)); + pgeoms.push_back(prep::PreparedGeometryFactory::prepare(geoms.back().get())); + } + } + + auto findIntersects = [&geoms](const PreparedGeometry* pg) { + for (const auto& geom : geoms) { + pg->intersects(geom.get()); + } + }; + + for (std::size_t i = 0; i < nthreads; i++) { + threads.emplace_back(findIntersects, pgeoms[i].get()); + } + + for (auto& thread : threads) { + thread.join(); + } + +} + } // namespace tut