We have a rather large application that queries a PostgreSQL database
from multiple threads and creates Shapely/GEOS geometry objects from
multiple threads using the WKB builder. Over the past few weeks we
have been trying to track down a odd crash in our application that
seems to happen when we create a Shapely/GEOS object from WKT in order
to start the DB query. After many hours of debugging we believe we
have a solution. From what we can tell our application was crashing
because memory that was being allocated in the GEOS dll was being
released inside of our application. When converting a GEOS geometry
object to a string, Shapely calls GEOSGeomToWKT_r which creates a char
buffer using std::malloc. After the call Shapely releases the memory
using the standard free function. According to a "Potential Errors
Passing CRT Objects Across DLL Boundaries" [1]:
"A related problem can occur when you allocate memory (either
explicitly with new or malloc, or implicitly with strdup,
strstreambuf::str, and so on) and then pass a pointer across a DLL
boundary to be freed. This can cause a memory access violation or heap
corruption if the DLL and its users use different copies of the CRT
libraries.
The basic problem is that two DLLs can use different copies of the
CRT. This can lead to many problems if you don't allocated and free
memory in the same DLL. In order to get around this problem we added a
new function to the GEOS capi, GEOSFree, that simply frees memory
allocated in the GEOS DLL. Then in Shapely we just use this function
where we need to ensure that memory allocated and returned from GEOS
is also freed in the GEOS DLL.
I have attached a patch for both GEOS and Shapely that we have been
testing with for a few weeks without any problems. Does anyone have a
better way to address this issue?
Thanks,
Aron Bierbaum
Index: capi/geos_c.cpp
===================================================================
--- capi/geos_c.cpp (revision 1422)
+++ capi/geos_c.cpp (revision 1423)
@@ -85,6 +85,12 @@
finishGEOS_r( handle );
}
+void
+GEOSFree (void* buffer)
+{
+ free(buffer);
+}
+
/****************************************************************
** relate()-related functions
** return 0 = false, 1 = true, 2 = error occured
Index: capi/geos_c.h
===================================================================
--- capi/geos_c.h (revision 1422)
+++ capi/geos_c.h (revision 1423)
@@ -145,6 +145,7 @@
extern void GEOS_DLL initGEOS(GEOSMessageHandler notice_function,
GEOSMessageHandler error_function);
extern void GEOS_DLL finishGEOS(void);
+extern void GEOS_DLL GEOSFree(void* buffer);
extern GEOSContextHandle_t GEOS_DLL initGEOS_r(
GEOSMessageHandler notice_function,
Index: shapely/geos.py
===================================================================
--- shapely/geos.py (revision 1424)
+++ shapely/geos.py (revision 1425)
@@ -134,6 +134,9 @@
if 'geos_handle' == name:
self.geos_handle = lgeos._lgeos.initGEOS_r(notice_h, error_h)
return self.geos_handle
+ if name == 'GEOSFree':
+ attr = getattr(self._lgeos, name)
+ return attr
old_func = getattr(self._lgeos, name)
if geos_c_version >= (1,5,0):
@@ -162,7 +165,7 @@
def errcheck_just_free(result, func, argtuple):
retval = result.value
- free(result)
+ lgeos.GEOSFree(result)
return retval
func = lgeos.GEOSGeomToWKT
Index: shapely/ctypes_declarations.py
===================================================================
--- shapely/ctypes_declarations.py (revision 1424)
+++ shapely/ctypes_declarations.py (revision 1425)
@@ -6,6 +6,9 @@
_lgeos.finishGEOS.restype = None
+_lgeos.GEOSFree.restype = None
+_lgeos.GEOSFree.argtypes = [ctypes.c_void_p]
+
_lgeos.GEOSversion.restype = ctypes.c_char_p
_lgeos.GEOSGeomFromWKT.restype = ctypes.c_void_p
_______________________________________________
Community mailing list
[email protected]
http://lists.gispython.org/mailman/listinfo/community