From: Paul Ramsey Date: Mon, 18 Jun 2012 17:12:22 +0000 (+0000) Subject: Update caching code to be more generic, using call-backs to allocate/build/free speci... X-Git-Tag: 2.1.0beta2~896 X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=e16cba24b13c2b6c21288ec8fa983df2f1f5241e;p=postgis Update caching code to be more generic, using call-backs to allocate/build/free specific cache structures. git-svn-id: http://svn.osgeo.org/postgis/trunk@9928 b70326c6-7e19-0410-871a-916f4a2858ee --- diff --git a/libpgcommon/lwgeom_cache.c b/libpgcommon/lwgeom_cache.c index 59063715b..a4f55dbe2 100644 --- a/libpgcommon/lwgeom_cache.c +++ b/libpgcommon/lwgeom_cache.c @@ -119,6 +119,7 @@ GetPROJ4SRSCache(FunctionCallInfoData* fcinfo) * GeomCache entry from the generic cache if one exists. * If it doesn't exist, make a new empty one and return it. */ +#if 0 GeomCache* GetGeomCache(FunctionCallInfoData* fcinfo, int cache_entry) { @@ -154,8 +155,112 @@ GetGeomCache(FunctionCallInfoData* fcinfo, int cache_entry) return cache; } +#endif +GeomCache* +GetGeomCache2(FunctionCallInfoData* fcinfo, const GeomCacheMethods* cache_methods, const GSERIALIZED* g1, const GSERIALIZED* g2) +{ + GeomCache* cache; + int cache_hit = 0; + MemoryContext old_context; + const GSERIALIZED *geom; + GenericCacheCollection* generic_cache = GetGenericCacheCollection(fcinfo); + int entry_number = cache_methods->entry_number; + + Assert(entry_number >= 0); + Assert(entry_number < NUM_CACHE_ENTRIES); + + cache = (GeomCache*)(generic_cache->entry[entry_number]); + + if ( ! cache ) + { + old_context = MemoryContextSwitchTo(FIContext(fcinfo)); + /* Allocate in the upper context */ + cache = cache_methods->GeomCacheAllocator(); + MemoryContextSwitchTo(old_context); + /* Store the pointer in GenericCache */ + cache->type = entry_number; + generic_cache->entry[entry_number] = (GenericCache*)cache; + } + + /* Cache hit on the first argument */ + if ( g1 && + cache->argnum != 2 && + cache->geom1_size == VARSIZE(g1) && + memcmp(cache->geom1, g1, cache->geom1_size) == 0 ) + { + cache_hit = 1; + geom = g1; + + } + /* Cache hit on second argument */ + else if ( g2 && + cache->argnum != 1 && + cache->geom2_size == VARSIZE(g2) && + memcmp(cache->geom2, g2, cache->geom2_size) == 0 ) + { + cache_hit = 2; + geom = g2; + } + /* No cache hit. If we have a tree, free it. */ + else + { + cache_hit = 0; + if ( cache->argnum ) + { + cache_methods->GeomIndexFreer(cache); + cache->argnum = 0; + } + } + + /* Cache hit, but no tree built yet, build it! */ + if ( cache_hit && ! cache->argnum ) + { + int rv; + LWGEOM *lwgeom = lwgeom_from_gserialized(geom); + + /* Can't build a tree on a NULL or empty */ + if ( (!lwgeom) || lwgeom_is_empty(lwgeom) ) + return NULL; + + old_context = MemoryContextSwitchTo(FIContext(fcinfo)); + rv = cache_methods->GeomIndexBuilder(lwgeom, cache); + MemoryContextSwitchTo(old_context); + cache->argnum = cache_hit; + + /* Something went awry in the tree build phase */ + if ( ! rv ) + { + cache->argnum = 0; + return NULL; + } + + } + + /* We have a hit and a calculated tree, we're done */ + if ( cache_hit && cache->argnum ) + return cache; + + /* Argument one didn't match, so copy the new value in. */ + if ( g1 && cache_hit != 1 ) + { + if ( cache->geom1 ) pfree(cache->geom1); + cache->geom1_size = VARSIZE(g1); + cache->geom1 = MemoryContextAlloc(FIContext(fcinfo), cache->geom1_size); + memcpy(cache->geom1, g1, cache->geom1_size); + } + /* Argument two didn't match, so copy the new value in. */ + if ( g2 && cache_hit != 2 ) + { + if ( cache->geom2 ) pfree(cache->geom2); + cache->geom2_size = VARSIZE(g2); + cache->geom2 = MemoryContextAlloc(FIContext(fcinfo), cache->geom2_size); + memcpy(cache->geom2, g2, cache->geom2_size); + } + + return NULL; +} @@ -164,6 +269,7 @@ GetGeomCache(FunctionCallInfoData* fcinfo, int cache_entry) * and the geometry values. Checks for a cache, checks for cache hits, * returns a built tree if one exists. */ +#if 0 void* GetGeomIndex(FunctionCallInfoData* fcinfo, int cache_entry, GeomIndexBuilder index_build, GeomIndexFreer index_free, const GSERIALIZED* geom1, const GSERIALIZED* geom2, int* argnum) { @@ -258,7 +364,7 @@ GetGeomIndex(FunctionCallInfoData* fcinfo, int cache_entry, GeomIndexBuilder ind return NULL; } - +#endif /** * Builder, freeer and public accessor for cached CIRC_NODE trees @@ -266,43 +372,68 @@ GetGeomIndex(FunctionCallInfoData* fcinfo, int cache_entry, GeomIndexBuilder ind static int CircTreeBuilder(const LWGEOM* lwgeom, GeomCache* cache) { + CircTreeGeomCache* circ_cache = (CircTreeGeomCache*)cache; CIRC_NODE* tree = lwgeom_calculate_circ_tree(lwgeom); - if ( cache->index ) + + if ( circ_cache->index ) { - circ_tree_free((CIRC_NODE*)(cache->index)); - cache->index = 0; + circ_tree_free(circ_cache->index); + circ_cache->index = 0; } if ( ! tree ) return LW_FAILURE; - cache->index = (void*)tree; + circ_cache->index = tree; return LW_SUCCESS; } static int CircTreeFreer(GeomCache* cache) { - CIRC_NODE* tree = (CIRC_NODE*)(cache->index); - if ( tree ) + CircTreeGeomCache* circ_cache = (CircTreeGeomCache*)cache; + if ( circ_cache->index ) { - circ_tree_free(tree); - cache->index = 0; + circ_tree_free(circ_cache->index); + circ_cache->index = 0; + circ_cache->argnum = 0; } return LW_SUCCESS; } +static GeomCache* +CircTreeAllocator(void) +{ + CircTreeGeomCache* cache = palloc(sizeof(CircTreeGeomCache)); + memset(cache, 0, sizeof(CircTreeGeomCache)); + return (GeomCache*)cache; +} + +static GeomCacheMethods CircTreeCacheMethods = +{ + CIRC_CACHE_ENTRY, + CircTreeBuilder, + CircTreeFreer, + CircTreeAllocator +}; + CIRC_NODE* -GetCircTree(FunctionCallInfoData* fcinfo, GSERIALIZED* g1, GSERIALIZED* g2, int* argnum_of_cache) +GetCircTree(FunctionCallInfoData* fcinfo, const GSERIALIZED* g1, const GSERIALIZED* g2, int* argnum_of_cache) { - int argnum = 0; - CIRC_NODE* tree = NULL; + CircTreeGeomCache* cache = (CircTreeGeomCache*)GetGeomCache2(fcinfo, &CircTreeCacheMethods, g1, g2); - tree = (CIRC_NODE*)GetGeomIndex(fcinfo, CIRC_CACHE_ENTRY, CircTreeBuilder, CircTreeFreer, g1, g2, &argnum); + if ( ! cache ) + return NULL; if ( argnum_of_cache ) - *argnum_of_cache = argnum; + *argnum_of_cache = cache->argnum; - return tree; + return cache->index; +} + +CircTreeGeomCache* +GetCircTreeGeomCache(FunctionCallInfoData* fcinfo, const GSERIALIZED* g1, const GSERIALIZED* g2) +{ + return (CircTreeGeomCache*)GetGeomCache2(fcinfo, &CircTreeCacheMethods, g1, g2); } diff --git a/libpgcommon/lwgeom_cache.h b/libpgcommon/lwgeom_cache.h index 4bb2571cc..83674fc86 100644 --- a/libpgcommon/lwgeom_cache.h +++ b/libpgcommon/lwgeom_cache.h @@ -28,6 +28,7 @@ #define RTREE_CACHE_ENTRY 2 #define CIRC_CACHE_ENTRY 3 #define RECT_CACHE_ENTRY 4 + #define NUM_CACHE_ENTRIES 5 @@ -38,17 +39,24 @@ * argument the tree is built for. */ typedef struct { - int type; - GSERIALIZED* geom1; - GSERIALIZED* geom2; - size_t geom1_size; - size_t geom2_size; - int32 argnum; - MemoryContext context_statement; - MemoryContext context_callback; - void* index; + int type; + GSERIALIZED* geom1; + GSERIALIZED* geom2; + size_t geom1_size; + size_t geom2_size; + int32 argnum; } GeomCache; +typedef struct { + int type; // + GSERIALIZED* geom1; // + GSERIALIZED* geom2; // + size_t geom1_size; // + size_t geom2_size; // + int32 argnum; // + CIRC_NODE* index; +} CircTreeGeomCache; + /* An entry in the PROJ4 SRS cache */ typedef struct struct_PROJ4SRSCacheItem @@ -81,21 +89,33 @@ PROJ4PortalCache; * geometry and return a tree structure for fast edge * access. */ +#if 0 typedef int (*GeomIndexBuilder)(const LWGEOM* lwgeom, GeomCache* cache); typedef int (*GeomIndexFreer)(GeomCache* cache); +typedef GeomCache* (*GeomCacheAllocator)(void); +#endif + +typedef struct +{ + int entry_number; + int (*GeomIndexBuilder)(const LWGEOM* lwgeom, GeomCache* cache); + int (*GeomIndexFreer)(GeomCache* cache); + GeomCache* (*GeomCacheAllocator)(void); +} GeomCacheMethods; /* * Cache retrieval functions */ -PROJ4PortalCache* GetPROJ4SRSCache(FunctionCallInfoData *fcinfo); -GeomCache* GetGeomCache(FunctionCallInfoData *fcinfo, int cache_entry); -CIRC_NODE* GetCircTree(FunctionCallInfoData* fcinfo, GSERIALIZED* g1, GSERIALIZED* g2, int* argnum_of_cache); +PROJ4PortalCache* GetPROJ4SRSCache(FunctionCallInfoData *fcinfo); +GeomCache* GetGeomCache2(FunctionCallInfoData *fcinfo, const GeomCacheMethods* cache_methods, const GSERIALIZED* g1, const GSERIALIZED* g2); +CIRC_NODE* GetCircTree(FunctionCallInfoData* fcinfo, const GSERIALIZED* g1, const GSERIALIZED* g2, int* argnum_of_cache); +CircTreeGeomCache* GetCircTreeGeomCache(FunctionCallInfoData* fcinfo, const GSERIALIZED* g1, const GSERIALIZED* g2); /* * Given candidate geometries, a builer function and an entry type, cache and/or return an * appropriate tree. */ -void* GetGeomIndex(FunctionCallInfoData* fcinfo, int cache_entry, GeomIndexBuilder index_build, GeomIndexFreer tree_free, const GSERIALIZED* g1, const GSERIALIZED* g2, int* argnum); +//void* GetGeomIndex(FunctionCallInfoData* fcinfo, int cache_entry, GeomIndexBuilder index_build, GeomIndexFreer tree_free, const GSERIALIZED* g1, const GSERIALIZED* g2, int* argnum); #endif /* LWGEOM_CACHE_H_ */ diff --git a/postgis/lwgeom_geos_prepared.c b/postgis/lwgeom_geos_prepared.c index f722c6180..0a613ea83 100644 --- a/postgis/lwgeom_geos_prepared.c +++ b/postgis/lwgeom_geos_prepared.c @@ -281,7 +281,7 @@ DeletePrepGeomHashEntry(MemoryContext mcxt) static int PrepGeomCacheBuilder(const LWGEOM *lwgeom, GeomCache *cache) { - PrepGeomCache* prepcache; + PrepGeomCache* prepcache = (PrepGeomCache*)cache; PrepGeomHashEntry* pghe; /* @@ -293,14 +293,14 @@ PrepGeomCacheBuilder(const LWGEOM *lwgeom, GeomCache *cache) /* * No callback entry for this statement context yet? Set it up */ - if ( ! cache->context_callback ) + if ( ! prepcache->context_callback ) { PrepGeomHashEntry pghe; - cache->context_callback = MemoryContextCreate(T_AllocSetContext, 8192, + prepcache->context_callback = MemoryContextCreate(T_AllocSetContext, 8192, &PreparedCacheContextMethods, - cache->context_statement, + prepcache->context_statement, "PostGIS Prepared Geometry Context"); - pghe.context = cache->context_callback; + pghe.context = prepcache->context_callback; pghe.geom = 0; pghe.prepared_geom = 0; AddPrepGeomHashEntry( pghe ); @@ -310,19 +310,12 @@ PrepGeomCacheBuilder(const LWGEOM *lwgeom, GeomCache *cache) * Hum, we shouldn't be asked to build a new cache on top of * an existing one. Error. */ - if ( cache->index ) + if ( prepcache->argnum || prepcache->geom || prepcache->prepared_geom ) { lwerror("PrepGeomCacheBuilder asked to build new prepcache where one already exists."); return LW_FAILURE; } - /* - * Allocate a new index structure - */ - cache->index = MemoryContextAlloc(cache->context_statement, sizeof(PrepGeomCache)); - memset(cache->index, 0, sizeof(PrepGeomCache)); - prepcache = (PrepGeomCache*)(cache->index); - prepcache->geom = LWGEOM2GEOS( lwgeom ); if ( ! prepcache->geom ) return LW_FAILURE; prepcache->prepared_geom = GEOSPrepare( prepcache->geom ); @@ -333,10 +326,10 @@ PrepGeomCacheBuilder(const LWGEOM *lwgeom, GeomCache *cache) * In order to find the objects we need to destroy, we keep * extra references in a global hash object. */ - pghe = GetPrepGeomHashEntry(cache->context_callback); + pghe = GetPrepGeomHashEntry(prepcache->context_callback); if ( ! pghe ) { - lwerror("PrepGeomCacheBuilder failed to find hash entry for context %p", cache->context_callback); + lwerror("PrepGeomCacheBuilder failed to find hash entry for context %p", prepcache->context_callback); return LW_FAILURE; } @@ -360,7 +353,7 @@ static int PrepGeomCacheCleaner(GeomCache *cache) { PrepGeomHashEntry* pghe = 0; - PrepGeomCache* prepcache = (PrepGeomCache*)(cache->index); + PrepGeomCache* prepcache = (PrepGeomCache*)cache; if ( ! prepcache ) return LW_FAILURE; @@ -369,10 +362,10 @@ PrepGeomCacheCleaner(GeomCache *cache) * Clear out the references to the soon-to-be-freed GEOS objects * from the callback hash entry */ - pghe = GetPrepGeomHashEntry(cache->context_callback); + pghe = GetPrepGeomHashEntry(prepcache->context_callback); if ( ! pghe ) { - lwerror("PrepGeomCacheCleaner failed to find hash entry for context %p", cache->context_callback); + lwerror("PrepGeomCacheCleaner failed to find hash entry for context %p", prepcache->context_callback); return LW_FAILURE; } pghe->geom = 0; @@ -384,12 +377,32 @@ PrepGeomCacheCleaner(GeomCache *cache) POSTGIS_DEBUGF(3, "PrepGeomCacheFreeer: freeing %p argnum %d", prepcache, prepcache->argnum); GEOSPreparedGeom_destroy( prepcache->prepared_geom ); GEOSGeom_destroy( (GEOSGeometry *)prepcache->geom ); - pfree(cache->index); - cache->index = 0; + prepcache->argnum = 0; + prepcache->prepared_geom = 0; + prepcache->geom = 0; return LW_SUCCESS; } +static GeomCache* +PrepGeomCacheAllocator() +{ + PrepGeomCache* prepcache = palloc(sizeof(PrepGeomCache)); + memset(prepcache, 0, sizeof(PrepGeomCache)); + prepcache->context_statement = CurrentMemoryContext; + prepcache->type = PREP_CACHE_ENTRY; + return (GeomCache*)prepcache; +} + +static GeomCacheMethods PrepGeomCacheMethods = +{ + PREP_CACHE_ENTRY, + PrepGeomCacheBuilder, + PrepGeomCacheCleaner, + PrepGeomCacheAllocator +}; + + /** * Given a couple potential geometries and a function * call context, return a prepared structure for one @@ -404,11 +417,6 @@ PrepGeomCacheCleaner(GeomCache *cache) PrepGeomCache* GetPrepGeomCache(FunctionCallInfoData* fcinfo, GSERIALIZED* g1, GSERIALIZED* g2) { - int argnum = 0; - PrepGeomCache* prepcache = NULL; - - prepcache = (PrepGeomCache*)GetGeomIndex(fcinfo, PREP_CACHE_ENTRY, PrepGeomCacheBuilder, PrepGeomCacheCleaner, g1, g2, &argnum); - - return prepcache; + return (PrepGeomCache*)GetGeomCache2(fcinfo, &PrepGeomCacheMethods, g1, g2); } diff --git a/postgis/lwgeom_geos_prepared.h b/postgis/lwgeom_geos_prepared.h index e100ba8fe..0fabf36be 100644 --- a/postgis/lwgeom_geos_prepared.h +++ b/postgis/lwgeom_geos_prepared.h @@ -47,7 +47,7 @@ typedef struct } PrepGeomCache; */ - +/* typedef struct { int argnum; @@ -55,6 +55,20 @@ typedef struct const GEOSGeometry *geom; } PrepGeomCache; +*/ +typedef struct { + int type; // + GSERIALIZED* geom1; // + GSERIALIZED* geom2; // + size_t geom1_size; // + size_t geom2_size; // + int32 argnum; // + MemoryContext context_statement; + MemoryContext context_callback; + const GEOSPreparedGeometry* prepared_geom; + const GEOSGeometry* geom; +} PrepGeomCache; + /* ** Get the current cache, given the input geometries. diff --git a/postgis/lwgeom_rtree.c b/postgis/lwgeom_rtree.c index 7bef46bb7..1408c5e78 100644 --- a/postgis/lwgeom_rtree.c +++ b/postgis/lwgeom_rtree.c @@ -305,10 +305,17 @@ RTreeBuilder(const LWGEOM* lwgeom, GeomCache* cache) LWMPOLY *mpoly; LWPOLY *poly; int nrings; + RTreeGeomCache* rtree_cache = (RTreeGeomCache*)cache; RTREE_POLY_CACHE* currentCache; if ( ! cache ) return LW_FAILURE; + + if ( rtree_cache->index ) + { + lwerror("RTreeBuilder asked to build index where one already exists."); + return LW_FAILURE; + } if (lwgeom->type == MULTIPOLYGONTYPE) { @@ -340,7 +347,7 @@ RTreeBuilder(const LWGEOM* lwgeom, GeomCache* cache) i++; } } - cache->index = (void*)currentCache; + rtree_cache->index = currentCache; } else if ( lwgeom->type == POLYGONTYPE ) { @@ -358,7 +365,7 @@ RTreeBuilder(const LWGEOM* lwgeom, GeomCache* cache) { currentCache->ringIndices[i] = RTreeCreate(poly->rings[i]); } - cache->index = (void*)currentCache; + rtree_cache->index = currentCache; } else { @@ -376,26 +383,45 @@ RTreeBuilder(const LWGEOM* lwgeom, GeomCache* cache) static int RTreeFreer(GeomCache* cache) { + RTreeGeomCache* rtree_cache = (RTreeGeomCache*)cache; + if ( ! cache ) return LW_FAILURE; - if ( cache->index ) + if ( rtree_cache->index ) { - RTREE_POLY_CACHE* currentCache = (RTREE_POLY_CACHE*)(cache->index); - RTreeCacheClear(currentCache); - lwfree(currentCache); - cache->index = 0; + RTreeCacheClear(rtree_cache->index); + lwfree(rtree_cache->index); + rtree_cache->index = 0; + rtree_cache->argnum = 0; } return LW_SUCCESS; } +static GeomCache* +RTreeAllocator(void) +{ + RTreeGeomCache* cache = palloc(sizeof(RTreeGeomCache)); + memset(cache, 0, sizeof(RTreeGeomCache)); + return (GeomCache*)cache; +} + +static GeomCacheMethods RTreeCacheMethods = +{ + RTREE_CACHE_ENTRY, + RTreeBuilder, + RTreeFreer, + RTreeAllocator +}; + RTREE_POLY_CACHE* GetRtreeCache(FunctionCallInfoData* fcinfo, GSERIALIZED* g1) { - int argnum = 0; + RTreeGeomCache* cache = (RTreeGeomCache*)GetGeomCache2(fcinfo, &RTreeCacheMethods, g1, NULL); RTREE_POLY_CACHE* index = NULL; - index = (RTREE_POLY_CACHE*)GetGeomIndex(fcinfo, RTREE_CACHE_ENTRY, RTreeBuilder, RTreeFreer, g1, 0, &argnum); + if ( cache ) + index = cache->index; return index; } diff --git a/postgis/lwgeom_rtree.h b/postgis/lwgeom_rtree.h index bf1ae9d76..12a986e81 100644 --- a/postgis/lwgeom_rtree.h +++ b/postgis/lwgeom_rtree.h @@ -38,6 +38,19 @@ typedef struct } RTREE_POLY_CACHE; + + +typedef struct { + int type; // + GSERIALIZED* geom1; // + GSERIALIZED* geom2; // + size_t geom1_size; // + size_t geom2_size; // + int32 argnum; // + RTREE_POLY_CACHE* index; +} RTreeGeomCache; + + /** * Retrieves a collection of line segments given the root and crossing value. */