Skip to content

Commit d7e2e99

Browse files
Merge pull request #9 from c42f/cjf/gc-support
Allow function body ASTs to be GC'd
2 parents e51174f + e186934 commit d7e2e99

File tree

1 file changed

+21
-18
lines changed

1 file changed

+21
-18
lines changed

src/RuntimeGeneratedFunctions.jl

Lines changed: 21 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -10,13 +10,15 @@ export @RuntimeGeneratedFunction
1010
1111
This type should be constructed via the macro @RuntimeGeneratedFunction.
1212
"""
13-
struct RuntimeGeneratedFunction{moduletag,id,argnames}
13+
mutable struct RuntimeGeneratedFunction{moduletag,id,argnames}
14+
body::Expr
1415
function RuntimeGeneratedFunction(moduletag, ex)
16+
id = expr2bytes(ex)
1517
def = splitdef(ex)
1618
args, body = normalize_args(def[:args]), def[:body]
17-
id = expr2bytes(body)
18-
_cache_body(moduletag, id, body)
19-
new{moduletag,id,Tuple(args)}()
19+
f = new{moduletag,id,Tuple(args)}(body)
20+
_cache_self(moduletag, id, f)
21+
return f
2022
end
2123
end
2224

@@ -51,31 +53,30 @@ macro RuntimeGeneratedFunction(ex)
5153
end
5254

5355
function Base.show(io::IO, f::RuntimeGeneratedFunction{moduletag, id, argnames}) where {moduletag,id,argnames}
54-
body = _lookup_body(moduletag, id)
5556
mod = parentmodule(moduletag)
56-
func_expr = Expr(:->, Expr(:tuple, argnames...), body)
57+
func_expr = Expr(:->, Expr(:tuple, argnames...), f.body)
5758
print(io, "RuntimeGeneratedFunction(#=in $mod=#, ", repr(func_expr), ")")
5859
end
5960

6061
(f::RuntimeGeneratedFunction)(args::Vararg{Any,N}) where N = generated_callfunc(f, args...)
6162

6263
@inline @generated function generated_callfunc(f::RuntimeGeneratedFunction{moduletag, id, argnames}, __args...) where {moduletag,id,argnames}
6364
setup = (:($(argnames[i]) = @inbounds __args[$i]) for i in 1:length(argnames))
65+
f_value = _lookup_self(moduletag, id)
6466
quote
6567
$(setup...)
66-
$(_lookup_body(moduletag, id))
68+
$(f_value.body)
6769
end
6870
end
6971

70-
### Function body caching and lookup
72+
### Function caching and lookup
7173
#
72-
# Caching the body of a RuntimeGeneratedFunction is a little complicated
73-
# because we want the `id=>body` mapping to survive precompilation. This means
74-
# we need to store the cache of mappings which are created by a module in that
75-
# module itself.
74+
# Looking up a RuntimeGeneratedFunction based on the id is a little complicated
75+
# because we want the `id=>func` mapping to survive precompilation. This means
76+
# we need to store the mapping created by a module in that module itself.
7677
#
7778
# For that, we need a way to lookup the correct module from an instance of
78-
# RuntimeGeneratedFunction. Modules can't be type parameters, but we can use
79+
# RuntimeGeneratedFunction. Modules can't be type parameters, but we can use
7980
# any type which belongs to the module as a proxy "tag" for the module.
8081
#
8182
# (We could even abuse `typeof(__module__.eval)` for the tag, though this is a
@@ -85,18 +86,20 @@ end
8586
_cachename = Symbol("#_RuntimeGeneratedFunctions_cache")
8687
_tagname = Symbol("#_RuntimeGeneratedFunctions_ModTag")
8788

88-
function _cache_body(moduletag, id, body)
89-
getfield(parentmodule(moduletag), _cachename)[id] = body
89+
function _cache_self(moduletag, id, f)
90+
# Use a WeakRef to allow `f` to be garbage collected. (After GC the cache
91+
# will still contain an empty entry with key `id`.)
92+
getfield(parentmodule(moduletag), _cachename)[id] = WeakRef(f)
9093
end
9194

92-
function _lookup_body(moduletag, id)
93-
getfield(parentmodule(moduletag), _cachename)[id]
95+
function _lookup_self(moduletag, id)
96+
getfield(parentmodule(moduletag), _cachename)[id].value
9497
end
9598

9699
function _ensure_cache_exists!(mod)
97100
if !isdefined(mod, _cachename)
98101
mod.eval(quote
99-
const $_cachename = Dict{Tuple,Expr}()
102+
const $_cachename = Dict()
100103
struct $_tagname
101104
end
102105
end)

0 commit comments

Comments
 (0)