Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Allow freezing of Core.MethodTables #56143

Open
wants to merge 20 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 9 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
31 changes: 31 additions & 0 deletions base/runtime_internals.jl
Original file line number Diff line number Diff line change
Expand Up @@ -1182,6 +1182,37 @@ function signature_type(@nospecialize(f), @nospecialize(argtypes))
return rewrap_unionall(Tuple{ft, u.parameters...}, argtypes)
end

"""
delete_method(m::Method)

Make method `m` uncallable and force recompilation of any methods that use(d) it.
"""
function delete_method(m::Method)
ccall(:jl_method_table_disable, Cvoid, (Any, Any), get_methodtable(m), m)
end

fatteneder marked this conversation as resolved.
Show resolved Hide resolved
"""
freeze!(mt::Core.MethodTable)

Disallow adding or modifying methods of `mt`.

See also [`unfreeze!(mt)`](@ref unfreeze!).
"""
function freeze!(mt::Core.MethodTable)
fatteneder marked this conversation as resolved.
Show resolved Hide resolved
ccall(:jl_method_table_set_frozen, Cvoid, (Any, Cint), mt, 1)
fatteneder marked this conversation as resolved.
Show resolved Hide resolved
end

"""
unfreeze!(mt::Core.MethodTable)

Allow adding or modifying methods of `mt`.

See also [`freeze!(mt)`](@ref freeze!).
"""
function unfreeze!(mt::Core.MethodTable)
ccall(:jl_method_table_set_frozen, Cvoid, (Any, Cint), mt, 0)
end

fatteneder marked this conversation as resolved.
Show resolved Hide resolved
function get_methodtable(m::Method)
mt = ccall(:jl_method_get_table, Any, (Any,), m)
if mt === nothing
Expand Down
13 changes: 12 additions & 1 deletion src/gf.c
Original file line number Diff line number Diff line change
Expand Up @@ -772,7 +772,7 @@ static int reset_mt_caches(jl_methtable_t *mt, void *env)
{
// removes all method caches
// this might not be entirely safe (GC or MT), thus we only do it very early in bootstrapping
if (!mt->frozen) { // make sure not to reset builtin functions
if (!mt->frozen) { // make sure not to reset frozen functions
jl_atomic_store_release(&mt->leafcache, (jl_genericmemory_t*)jl_an_empty_memory_any);
jl_atomic_store_release(&mt->cache, jl_nothing);
}
Expand Down Expand Up @@ -2028,6 +2028,17 @@ JL_DLLEXPORT void jl_method_table_disable(jl_methtable_t *mt, jl_method_t *metho
JL_UNLOCK(&world_counter_lock);
}

JL_DLLEXPORT void jl_method_table_set_frozen(jl_methtable_t *mt, int val)
{
JL_LOCK(&world_counter_lock);
fatteneder marked this conversation as resolved.
Show resolved Hide resolved
JL_LOCK(&mt->writelock);
size_t world = jl_atomic_load_relaxed(&jl_world_counter);
mt->frozen = val;
jl_atomic_store_release(&jl_world_counter, world + 1);
JL_UNLOCK(&mt->writelock);
JL_UNLOCK(&world_counter_lock);
fatteneder marked this conversation as resolved.
Show resolved Hide resolved
}

static int jl_type_intersection2(jl_value_t *t1, jl_value_t *t2, jl_value_t **isect JL_REQUIRE_ROOTED_SLOT, jl_value_t **isect2 JL_REQUIRE_ROOTED_SLOT)
{
*isect2 = NULL;
Expand Down
1 change: 1 addition & 0 deletions src/jl_exported_funcs.inc
Original file line number Diff line number Diff line change
Expand Up @@ -305,6 +305,7 @@
XX(jl_method_instance_add_backedge) \
XX(jl_method_table_add_backedge) \
XX(jl_method_table_disable) \
XX(jl_method_table_set_frozen) \
XX(jl_method_table_for) \
XX(jl_method_table_insert) \
XX(jl_methtable_lookup) \
Expand Down
2 changes: 1 addition & 1 deletion src/method.c
Original file line number Diff line number Diff line change
Expand Up @@ -1227,7 +1227,7 @@ JL_DLLEXPORT jl_method_t* jl_method_def(jl_svec_t *argdata,
if ((jl_value_t*)mt == jl_nothing)
jl_error("Method dispatch is unimplemented currently for this method signature");
if (mt->frozen)
jl_error("cannot add methods to a builtin function");
jl_error("cannot add methods to or modify methods of a frozen function");

assert(jl_is_linenode(functionloc));
jl_sym_t *file = (jl_sym_t*)jl_linenode_file(functionloc);
Expand Down
2 changes: 1 addition & 1 deletion test/core.jl
Original file line number Diff line number Diff line change
Expand Up @@ -2650,7 +2650,7 @@ for f in (:Any, :Function, :(Core.Builtin), :(Union{Nothing, Type}), :(Union{typ
@test_throws ErrorException("Method dispatch is unimplemented currently for this method signature") @eval (::$f)() = 1
end
for f in (:(Core.getfield), :((::typeof(Core.getfield))), :((::Core.IntrinsicFunction)))
@test_throws ErrorException("cannot add methods to a builtin function") @eval $f() = 1
@test_throws ErrorException("cannot add methods to or modify methods of a frozen function") @eval $f() = 1
end

# issue #33370
Expand Down
12 changes: 12 additions & 0 deletions test/reflection.jl
Original file line number Diff line number Diff line change
Expand Up @@ -1298,3 +1298,15 @@ end
@test Base.infer_return_type(code_lowered, (Any,Any)) == Vector{Core.CodeInfo}

@test methods(Union{}) == Any[m.method for m in Base._methods_by_ftype(Tuple{Core.TypeofBottom, Vararg}, 1, Base.get_world_counter())] # issue #55187

# freezing Core.MethodTable
f_frozen(x::Int) = x+1
Base.freeze!(Base.methods(f_frozen).mt)
@test_throws(
ErrorException("cannot add methods to or modify methods of a frozen function"),
@eval f_frozen(x::Float64) = x+2
)
fatteneder marked this conversation as resolved.
Show resolved Hide resolved
Base.unfreeze!(Base.methods(f_frozen).mt)
f_frozen(x::Float64) = x+2
@test f_frozen(1) == 2
@test f_frozen(1.0) == 3.0
fatteneder marked this conversation as resolved.
Show resolved Hide resolved