Skip to content

Commit 23f0222

Browse files
jw3126visr
andauthored
free objects in the right context (#139)
* free objects in the right context * remove get_context * add get_context * probe ci * fix * fix * fix STRtree memory leak * move STRtree docstring Co-authored-by: Martijn Visser <[email protected]>
1 parent 24724e0 commit 23f0222

File tree

4 files changed

+98
-41
lines changed

4 files changed

+98
-41
lines changed

src/geos_functions.jl

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -477,8 +477,11 @@ function cloneGeom(ptr::GEOSGeom, context::GEOSContext = _context)
477477
result
478478
end
479479

480-
destroyGeom(ptr::GEOSGeom, context::GEOSContext = _context) =
481-
GEOSGeom_destroy_r(context.ptr, ptr)
480+
function destroyGeom(ptr::GEOSGeom, context::GEOSContext = _context)
481+
GC.@preserve context begin
482+
GEOSGeom_destroy_r(context.ptr, ptr)
483+
end
484+
end
482485

483486
# -----
484487
# Topology operations - return NULL on exception.
@@ -797,8 +800,11 @@ function prepareGeom(ptr::GEOSGeom, context::GEOSContext = _context)
797800
result
798801
end
799802

800-
destroyPreparedGeom(ptr::Ptr{GEOSPreparedGeometry}, context::GEOSContext = _context) =
801-
GEOSPreparedGeom_destroy_r(context.ptr, ptr)
803+
function destroyPreparedGeom(ptr::Ptr{GEOSPreparedGeometry}, context::GEOSContext = _context)
804+
GC.@preserve context begin
805+
GEOSPreparedGeom_destroy_r(context.ptr, ptr)
806+
end
807+
end
802808

803809
function prepcontains(
804810
g1::Ptr{GEOSPreparedGeometry},

src/geos_types.jl

Lines changed: 34 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -2,11 +2,12 @@ abstract type AbstractGeometry end
22

33
mutable struct Point <: AbstractGeometry
44
ptr::GEOSGeom
5+
context::GEOSContext
56
# create a point from a pointer - only makese sense if it is a pointer to a point, otherwise error
67
function Point(ptr::GEOSGeom, context::GEOSContext = _context)
78
id = LibGEOS.geomTypeId(ptr, context)
89
point = if id == GEOS_POINT
9-
point = new(cloneGeom(ptr, context))
10+
point = new(cloneGeom(ptr, context), context)
1011
else
1112
error("LibGEOS: Can't convert a pointer to an element with a GeomType ID of $id to a point (yet).
1213
Please open an issue if you think this conversion makes sense.")
@@ -25,16 +26,19 @@ end
2526

2627
mutable struct MultiPoint <: AbstractGeometry
2728
ptr::GEOSGeom
29+
context::GEOSContext
2830
# create a multipoint from a pointer - only makes sense if it is a pointer to a multipoint
2931
# or to a point, otherwise error
3032
function MultiPoint(ptr::GEOSGeom, context::GEOSContext = _context)
3133
id = LibGEOS.geomTypeId(ptr, context)
3234
multipoint = if id == GEOS_MULTIPOINT
33-
new(cloneGeom(ptr, context))
35+
new(cloneGeom(ptr, context), context)
3436
elseif id == GEOS_POINT
3537
new(createCollection(GEOS_MULTIPOINT,
3638
GEOSGeom[cloneGeom(ptr, context)],
37-
context))
39+
context),
40+
context
41+
)
3842
else
3943
error("LibGEOS: Can't convert a pointer to an element with a GeomType ID of $id to a multipoint (yet).
4044
Please open an issue if you think this conversion makes sense.")
@@ -62,11 +66,12 @@ end
6266

6367
mutable struct LineString <: AbstractGeometry
6468
ptr::GEOSGeom
69+
context::GEOSContext
6570
# create a linestring from a linestring pointer, otherwise error
6671
function LineString(ptr::GEOSGeom, context::GEOSContext = _context)
6772
id = LibGEOS.geomTypeId(ptr, context)
6873
line = if id == GEOS_LINESTRING
69-
new(cloneGeom(ptr, context))
74+
new(cloneGeom(ptr, context), context)
7075
else
7176
error("LibGEOS: Can't convert a pointer to an element with a GeomType ID of $id to a linestring (yet).
7277
Please open an issue if you think this conversion makes sense.")
@@ -76,23 +81,24 @@ mutable struct LineString <: AbstractGeometry
7681
end
7782
#create a linestring from a list of coordiantes
7883
function LineString(coords::Vector{Vector{Float64}}, context::GEOSContext = _context)
79-
line = new(createLineString(coords, context))
84+
line = new(createLineString(coords, context), context)
8085
finalizer(destroyGeom, line)
8186
line
8287
end
8388
end
8489

8590
mutable struct MultiLineString <: AbstractGeometry
8691
ptr::GEOSGeom
92+
context::GEOSContext
8793
# create a multiline string from a multilinestring or a linestring pointer, else error
8894
function MultiLineString(ptr::GEOSGeom, context::GEOSContext = _context)
8995
id = LibGEOS.geomTypeId(ptr, context)
9096
multiline = if id == GEOS_MULTILINESTRING
91-
new(cloneGeom(ptr, context))
97+
new(cloneGeom(ptr, context), context)
9298
elseif id == GEOS_LINESTRING
9399
new(createCollection(GEOS_MULTILINESTRING,
94100
GEOSGeom[cloneGeom(ptr, context)],
95-
context))
101+
context), context)
96102
else
97103
error("LibGEOS: Can't convert a pointer to an element with a GeomType ID of $id to a multi-linestring (yet).
98104
Please open an issue if you think this conversion makes sense.")
@@ -112,11 +118,12 @@ end
112118

113119
mutable struct LinearRing <: AbstractGeometry
114120
ptr::GEOSGeom
121+
context::GEOSContext
115122
# create a linear ring from a linear ring pointer, otherwise error
116123
function LinearRing(ptr::GEOSGeom, context::GEOSContext = _context)
117124
id = LibGEOS.geomTypeId(ptr, context)
118125
ring = if id == GEOS_LINEARRING
119-
new(cloneGeom(ptr, context))
126+
new(cloneGeom(ptr, context), context)
120127
else
121128
error("LibGEOS: Can't convert a pointer to an element with a GeomType ID of $id to a linear ring (yet).
122129
Please open an issue if you think this conversion makes sense.")
@@ -127,7 +134,7 @@ mutable struct LinearRing <: AbstractGeometry
127134
# create linear ring from a list of coordinates -
128135
# first and last coordinates must be the same
129136
function LinearRing(coords::Vector{Vector{Float64}}, context::GEOSContext = _context)
130-
ring = new(createLinearRing(coords, context))
137+
ring = new(createLinearRing(coords, context), context)
131138
finalizer(destroyGeom, ring)
132139
ring
133140
end
@@ -136,13 +143,14 @@ end
136143

137144
mutable struct Polygon <: AbstractGeometry
138145
ptr::GEOSGeom
146+
context::GEOSContext
139147
# create polygon using GEOSGeom pointer - only makes sense if pointer points to a polygon or a linear ring to start with.
140148
function Polygon(ptr::GEOSGeom, context::GEOSContext = _context)
141149
id = LibGEOS.geomTypeId(ptr, context)
142150
polygon = if id == GEOS_POLYGON
143-
new(cloneGeom(ptr, context))
151+
new(cloneGeom(ptr, context), context)
144152
elseif id == GEOS_LINEARRING
145-
new(cloneGeom(createPolygon(ptr, context), context))
153+
new(cloneGeom(createPolygon(ptr, context), context), context)
146154
else
147155
error("LibGEOS: Can't convert a pointer to an element with a GeomType ID of $id to a polygon (yet).
148156
Please open an issue if you think this conversion makes sense.")
@@ -155,7 +163,7 @@ mutable struct Polygon <: AbstractGeometry
155163
function Polygon(coords::Vector{Vector{Vector{Float64}}}, context::GEOSContext = _context)
156164
exterior = createLinearRing(coords[1], context)
157165
interiors = GEOSGeom[createLinearRing(lr, context) for lr in coords[2:end]]
158-
polygon = new(createPolygon(exterior, interiors, context))
166+
polygon = new(createPolygon(exterior, interiors, context), context)
159167
finalizer(destroyGeom, polygon)
160168
polygon
161169
end
@@ -173,16 +181,19 @@ end
173181

174182
mutable struct MultiPolygon <: AbstractGeometry
175183
ptr::GEOSGeom
184+
context::GEOSContext
176185
# create multipolygon using a multipolygon or polygon pointer, else error
177186
function MultiPolygon(ptr::GEOSGeom, context::GEOSContext = _context)
178187
id = LibGEOS.geomTypeId(ptr, context)
179188
multipolygon = if id == GEOS_MULTIPOLYGON
180-
new(cloneGeom(ptr, context))
189+
new(cloneGeom(ptr, context), context)
181190
elseif id == GEOS_POLYGON
182191
new(createCollection(
183192
GEOS_MULTIPOLYGON,
184193
GEOSGeom[cloneGeom(ptr, context)],
185-
context))
194+
context),
195+
context
196+
)
186197
else
187198
error("LibGEOS: Can't convert a pointer to an element with a GeomType ID of $id to a multi-polygon (yet).
188199
Please open an issue if you think this conversion makes sense.")
@@ -217,11 +228,12 @@ end
217228

218229
mutable struct GeometryCollection <: AbstractGeometry
219230
ptr::GEOSGeom
231+
context::GEOSContext
220232
# create a geometric collection from a pointer to a geometric collection, else error
221233
function GeometryCollection(ptr::GEOSGeom, context::GEOSContext = _context)
222234
id = LibGEOS.geomTypeId(ptr, context)
223235
geometrycollection = if id == GEOS_GEOMETRYCOLLECTION
224-
new(cloneGeom(ptr, context))
236+
new(cloneGeom(ptr, context), context)
225237
else
226238
error("LibGEOS: Can't convert a pointer to an element with a GeomType ID of $id to a geometry collection (yet).
227239
Please open an issue if you think this conversion makes sense.")
@@ -250,7 +262,9 @@ const Geometry = Union{
250262
GeometryCollection,
251263
}
252264

253-
function destroyGeom(obj::Geometry, context::GEOSContext = _context)
265+
get_context(obj::Geometry) = obj.context
266+
function destroyGeom(obj::Geometry)
267+
context = get_context(obj)
254268
destroyGeom(obj.ptr, context)
255269
obj.ptr = C_NULL
256270
end
@@ -260,7 +274,10 @@ mutable struct PreparedGeometry{G<:AbstractGeometry} <: AbstractGeometry
260274
ownedby::G
261275
end
262276

263-
function destroyGeom(obj::PreparedGeometry, context::GEOSContext = _context)
277+
get_context(obj::PreparedGeometry) = get_context(obj.ownedby)
278+
279+
function destroyGeom(obj::PreparedGeometry)
280+
context = get_context(obj)
264281
destroyPreparedGeom(obj.ptr, context)
265282
obj.ptr = C_NULL
266283
end

src/strtree.jl

Lines changed: 34 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,3 @@
1-
struct STRtree{T}
2-
ptr::Ptr{GEOSSTRtree} # void pointer to the tree
3-
items::T # any geometry for which an envelope can be derived
4-
end
5-
61
"""
72
STRtree(items; nodecapacity=10, context::GEOSContext=_context)
83
@@ -15,13 +10,31 @@ envelope is defined. The tree cannot be changed after its creation.
1510
- `context`: The GEOS context in which the tree should be created in. Defaults to the global
1611
LibGEOS context.
1712
"""
18-
function STRtree(items; nodecapacity = 10, context::GEOSContext = _context)
19-
tree = LibGEOS.GEOSSTRtree_create_r(context.ptr, nodecapacity)
20-
for item in items
21-
envptr = envelope(item).ptr
22-
GEOSSTRtree_insert_r(context.ptr, tree, envptr, pointer_from_objref(item))
13+
mutable struct STRtree{T}
14+
ptr::Ptr{GEOSSTRtree} # void pointer to the tree
15+
items::T # any geometry for which an envelope can be derived
16+
context::GEOSContext
17+
function STRtree(items; nodecapacity = 10, context::GEOSContext = _context)
18+
tree = LibGEOS.GEOSSTRtree_create_r(context.ptr, nodecapacity)
19+
for item in items
20+
envptr = envelope(item).ptr
21+
GEOSSTRtree_insert_r(context.ptr, tree, envptr, pointer_from_objref(item))
22+
end
23+
T = typeof(items)
24+
ret = new{T}(tree, items, context)
25+
finalizer(destroySTRtree, ret)
26+
return ret
2327
end
24-
return STRtree(tree, items)
28+
end
29+
30+
get_context(obj::STRtree) = obj.context
31+
32+
function destroySTRtree(obj::STRtree)
33+
context = get_context(obj)
34+
GC.@preserve context begin
35+
GEOSSTRtree_destroy_r(context.ptr, obj.ptr)
36+
end
37+
obj.ptr = C_NULL
2538
end
2639

2740
"""
@@ -37,7 +50,7 @@ Returns the objects within `tree`, whose envelope intersects the envelope of `ge
3750
function query(
3851
tree::STRtree{T},
3952
geometry::Geometry;
40-
context::GEOSContext = _context,
53+
context::GEOSContext = get_context(tree),
4154
) where {T}
4255
matches = eltype(T)[]
4356

@@ -50,12 +63,14 @@ function query(
5063
end
5164

5265
cfun_callback = @cfunction($callback, Ptr{Cvoid}, (Ptr{Cvoid}, Ptr{Cvoid}))
53-
GEOSSTRtree_query_r(
54-
context.ptr,
55-
tree.ptr,
56-
geometry.ptr,
57-
cfun_callback,
58-
pointer_from_objref(matches),
59-
)
66+
GC.@preserve context tree geometry matches begin
67+
GEOSSTRtree_query_r(
68+
context.ptr,
69+
tree.ptr,
70+
geometry.ptr,
71+
cfun_callback,
72+
pointer_from_objref(matches),
73+
)
74+
end
6075
return matches
6176
end

test/test_geos_types.jl

Lines changed: 20 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -350,4 +350,23 @@ testValidTypeDims(multipoly::LibGEOS.MultiPolygon) =
350350
LibGEOS.destroyGeom(geomcollect_ctx)
351351
end
352352

353-
end
353+
end
354+
355+
@testset "Multi threading" begin
356+
function f91(n)
357+
# adapted from https://github.com/JuliaGeo/LibGEOS.jl/issues/91#issuecomment-1267732709
358+
contexts = [LibGEOS.GEOSContext() for i=1:Threads.nthreads()]
359+
p = [[[-1.,-1],[+1,-1],[+1,+1],[-1,+1],[-1,-1]]]
360+
Threads.@threads :static for i=1:n
361+
ctx = contexts[Threads.threadid()]
362+
g1 = LibGEOS.Polygon(p, ctx)
363+
g2 = LibGEOS.Polygon(p, ctx)
364+
for j=1:n
365+
LibGEOS.intersects(g1.ptr, g2.ptr, ctx)
366+
end
367+
end
368+
GC.gc(true)
369+
return nothing
370+
end
371+
f91(91)
372+
end

0 commit comments

Comments
 (0)