Skip to content

Commit

Permalink
PreparedPolygon: Use std::call_once for lazy initialization
Browse files Browse the repository at this point in the history
  • Loading branch information
dbaston committed Feb 14, 2023
1 parent fec3765 commit c196055
Show file tree
Hide file tree
Showing 3 changed files with 66 additions and 12 deletions.
10 changes: 8 additions & 2 deletions include/geos/geom/prep/PreparedPolygon.h
Original file line number Diff line number Diff line change
Expand Up @@ -50,13 +50,19 @@ namespace prep { // geos::geom::prep
*/
class PreparedPolygon : public BasicPreparedGeometry {
private:
bool isRectangle;
mutable std::unique_ptr<noding::FastSegmentSetIntersectionFinder> segIntFinder;
mutable std::unique_ptr<algorithm::locate::PointOnGeometryLocator> ptOnGeomLoc;
mutable std::unique_ptr<algorithm::locate::PointOnGeometryLocator> indexedPtOnGeomLoc;
mutable noding::SegmentString::ConstVect segStrings;
mutable std::unique_ptr<operation::distance::IndexedFacetDistance> 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);
Expand Down
27 changes: 17 additions & 10 deletions src/geom/prep/PreparedPolygon.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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<noding::FastSegmentSetIntersectionFinder>(&segStrings);
});

return segIntFinder.get();
}

Expand All @@ -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<algorithm::locate::SimplePointInAreaLocator>(&getGeometry());
if(!ptOnGeomLoc) {
std::call_once(ptOnGeomLocFlag, [this]() {
ptOnGeomLoc = detail::make_unique<algorithm::locate::SimplePointInAreaLocator>(&getGeometry());
});

return ptOnGeomLoc.get();
} else if (!indexedPtOnGeomLoc) {
indexedPtOnGeomLoc = detail::make_unique<algorithm::locate::IndexedPointInAreaLocator>(getGeometry());
}

std::call_once(indexedPtOnGeomLocFlag, [this]() {
indexedPtOnGeomLoc = detail::make_unique<algorithm::locate::IndexedPointInAreaLocator>(getGeometry());
});

return indexedPtOnGeomLoc.get();
}

Expand Down Expand Up @@ -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<operation::distance::IndexedFacetDistance>(&getGeometry());
});

return indexedDistance.get();
}

Expand Down
41 changes: 41 additions & 0 deletions tests/unit/geom/prep/PreparedGeometryTest.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
#include <geos/io/WKTReader.h>
// std
#include <memory>
#include <thread>

using namespace geos::geom;
using geos::geom::prep::PreparedGeometry;
Expand Down Expand Up @@ -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<std::unique_ptr<Geometry>> geoms;
std::vector<std::unique_ptr<PreparedGeometry>> pgeoms;
std::vector<std::thread> 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<double>(i), static_cast<double>(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

0 comments on commit c196055

Please sign in to comment.