From 4e03fea204eda7211dbcce9d54783225a8adc70d Mon Sep 17 00:00:00 2001 From: Drvi Date: Thu, 14 Nov 2024 12:55:49 +0100 Subject: [PATCH 1/5] Optionally disallow defining new methods and drop backedges --- src/gf.c | 45 +++++++++++++++++++++++++++++++++++++++++++++ src/staticdata.c | 4 ++-- 2 files changed, 47 insertions(+), 2 deletions(-) diff --git a/src/gf.c b/src/gf.c index 559a23fdda725..d2a7e96211efd 100644 --- a/src/gf.c +++ b/src/gf.c @@ -24,6 +24,7 @@ extern "C" { #endif +static int allow_new_worlds = 1; JL_DLLEXPORT _Atomic(size_t) jl_world_counter = 1; // uses atomic acquire/release JL_DLLEXPORT size_t jl_get_world_counter(void) JL_NOTSAFEPOINT { @@ -1718,6 +1719,8 @@ static void invalidate_backedges(void (*f)(jl_code_instance_t*), jl_method_insta // add a backedge from callee to caller JL_DLLEXPORT void jl_method_instance_add_backedge(jl_method_instance_t *callee, jl_value_t *invokesig, jl_method_instance_t *caller) { + if (!allow_new_worlds) + return; JL_LOCK(&callee->def.method->writelock); if (invokesig == jl_nothing) invokesig = NULL; // julia uses `nothing` but C uses NULL (#undef) @@ -1915,8 +1918,48 @@ static void jl_method_table_invalidate(jl_methtable_t *mt, jl_typemap_entry_t *m } } +static int erase_method_backedges(jl_typemap_entry_t *def, void *closure) +{ + jl_method_t *method = def->func.method; + jl_value_t *specializations = jl_atomic_load_relaxed(&method->specializations); + if (jl_is_svec(specializations)) { + size_t i, l = jl_svec_len(specializations); + for (i = 0; i < l; i++) { + jl_method_instance_t *mi = (jl_method_instance_t*)jl_svecref(specializations, i); + if ((jl_value_t*)mi != jl_nothing) { + mi->backedges = NULL; + } + } + } + else { + jl_method_instance_t *mi = (jl_method_instance_t*)specializations; + mi->backedges = NULL; + } + return 1; +} + +static int erase_all_backedges(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 + mt->backedges = NULL; + jl_typemap_visitor(jl_atomic_load_relaxed(&mt->defs), erase_method_backedges, env); + return 1; +} + +JL_DLLEXPORT void jl_disable_new_worlds(void) +{ + if (jl_generating_output()) + jl_error("Disabling Method changes is not possible when generating output."); + allow_new_worlds = 0; + jl_foreach_reachable_mtable(erase_all_backedges, (void*)NULL); +} + + JL_DLLEXPORT void jl_method_table_disable(jl_methtable_t *mt, jl_method_t *method) { + if (!allow_new_worlds) + jl_error("Method changes have been disabled via a call to jl_disable_new_worlds."); jl_typemap_entry_t *methodentry = do_typemap_search(mt, method); JL_LOCK(&mt->writelock); // Narrow the world age on the method to make it uncallable @@ -1986,6 +2029,8 @@ static int is_replacing(char ambig, jl_value_t *type, jl_method_t *m, jl_method_ JL_DLLEXPORT void jl_method_table_insert(jl_methtable_t *mt, jl_method_t *method, jl_tupletype_t *simpletype) { + if (!allow_new_worlds) + jl_error("Method changes have been disabled via a call to jl_disable_new_worlds."); JL_TIMING(ADD_METHOD, ADD_METHOD); assert(jl_is_method(method)); assert(jl_is_mtable(mt)); diff --git a/src/staticdata.c b/src/staticdata.c index fba0eb95c9ae1..13284384e0485 100644 --- a/src/staticdata.c +++ b/src/staticdata.c @@ -3447,11 +3447,11 @@ static jl_value_t *jl_restore_package_image_from_stream(void* pkgimage_handle, i jl_restore_system_image_from_stream_(f, image, depmods, checksum, (jl_array_t**)&restored, &init_order, &extext_methods, &new_specializations, &method_roots_list, &ext_targets, &edges, &base, &ccallable_list, &cachesizes); JL_SIGATOMIC_END(); - // Insert method extensions - jl_insert_methods(extext_methods); // No special processing of `new_specializations` is required because recaching handled it // Add roots to methods jl_copy_roots(method_roots_list, jl_worklist_key((jl_array_t*)restored)); + // Insert method extensions + jl_insert_methods(extext_methods); // Handle edges size_t world = jl_atomic_load_acquire(&jl_world_counter); jl_insert_backedges((jl_array_t*)edges, (jl_array_t*)ext_targets, (jl_array_t*)new_specializations, world); // restore external backedges (needs to be last) From a79cee8be189dcb9657bbdc3e61fe9c8d1566ac9 Mon Sep 17 00:00:00 2001 From: Drvi Date: Thu, 14 Nov 2024 14:12:03 +0100 Subject: [PATCH 2/5] Make `allow_new_worlds` atomic --- src/gf.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/gf.c b/src/gf.c index d2a7e96211efd..01587653d9645 100644 --- a/src/gf.c +++ b/src/gf.c @@ -24,7 +24,7 @@ extern "C" { #endif -static int allow_new_worlds = 1; +static _Atomic(size_t) allow_new_worlds = 1; JL_DLLEXPORT _Atomic(size_t) jl_world_counter = 1; // uses atomic acquire/release JL_DLLEXPORT size_t jl_get_world_counter(void) JL_NOTSAFEPOINT { @@ -1719,7 +1719,7 @@ static void invalidate_backedges(void (*f)(jl_code_instance_t*), jl_method_insta // add a backedge from callee to caller JL_DLLEXPORT void jl_method_instance_add_backedge(jl_method_instance_t *callee, jl_value_t *invokesig, jl_method_instance_t *caller) { - if (!allow_new_worlds) + if (!jl_atomic_load_acquire(&allow_new_worlds)) return; JL_LOCK(&callee->def.method->writelock); if (invokesig == jl_nothing) @@ -1951,14 +1951,14 @@ JL_DLLEXPORT void jl_disable_new_worlds(void) { if (jl_generating_output()) jl_error("Disabling Method changes is not possible when generating output."); - allow_new_worlds = 0; + jl_atomic_store_release(&allow_new_worlds, 0); jl_foreach_reachable_mtable(erase_all_backedges, (void*)NULL); } JL_DLLEXPORT void jl_method_table_disable(jl_methtable_t *mt, jl_method_t *method) { - if (!allow_new_worlds) + if (!jl_atomic_load_acquire(&allow_new_worlds)) jl_error("Method changes have been disabled via a call to jl_disable_new_worlds."); jl_typemap_entry_t *methodentry = do_typemap_search(mt, method); JL_LOCK(&mt->writelock); @@ -2029,7 +2029,7 @@ static int is_replacing(char ambig, jl_value_t *type, jl_method_t *m, jl_method_ JL_DLLEXPORT void jl_method_table_insert(jl_methtable_t *mt, jl_method_t *method, jl_tupletype_t *simpletype) { - if (!allow_new_worlds) + if (!jl_atomic_load_acquire(&allow_new_worlds)) jl_error("Method changes have been disabled via a call to jl_disable_new_worlds."); JL_TIMING(ADD_METHOD, ADD_METHOD); assert(jl_is_method(method)); From 917f8724c73d7ec8d50e2df0ed36ccc5c399f20b Mon Sep 17 00:00:00 2001 From: Drvi Date: Thu, 14 Nov 2024 17:26:20 +0100 Subject: [PATCH 3/5] Fixup: also respect `allow_new_world` in `jl_method_table_add_backedge` --- src/gf.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/gf.c b/src/gf.c index 01587653d9645..cbbe6b9c4ea2a 100644 --- a/src/gf.c +++ b/src/gf.c @@ -1756,6 +1756,8 @@ JL_DLLEXPORT void jl_method_instance_add_backedge(jl_method_instance_t *callee, // add a backedge from a non-existent signature to caller JL_DLLEXPORT void jl_method_table_add_backedge(jl_methtable_t *mt, jl_value_t *typ, jl_value_t *caller) { + if (!jl_atomic_load_acquire(&allow_new_worlds)) + return; JL_LOCK(&mt->writelock); if (!mt->backedges) { // lazy-init the backedges array From 2ab1309065cc210945c9acda636538a92010bea9 Mon Sep 17 00:00:00 2001 From: Drvi Date: Wed, 20 Nov 2024 18:43:07 +0100 Subject: [PATCH 4/5] make `allow_new_worlds` a `bool` --- src/gf.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/gf.c b/src/gf.c index cbbe6b9c4ea2a..fdd5b57b5b5a4 100644 --- a/src/gf.c +++ b/src/gf.c @@ -8,6 +8,7 @@ . static parameter inference . method specialization and caching, invoking type inference */ +#include #include #include #include "julia.h" @@ -24,7 +25,7 @@ extern "C" { #endif -static _Atomic(size_t) allow_new_worlds = 1; +static _Atomic(bool) allow_new_worlds = 1; JL_DLLEXPORT _Atomic(size_t) jl_world_counter = 1; // uses atomic acquire/release JL_DLLEXPORT size_t jl_get_world_counter(void) JL_NOTSAFEPOINT { From 775d60ec20569b4bbe0a844132c0ef1178f9809d Mon Sep 17 00:00:00 2001 From: Drvi Date: Thu, 21 Nov 2024 16:05:40 +0100 Subject: [PATCH 5/5] . --- src/gf.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/gf.c b/src/gf.c index fdd5b57b5b5a4..58050bf95cb11 100644 --- a/src/gf.c +++ b/src/gf.c @@ -25,7 +25,7 @@ extern "C" { #endif -static _Atomic(bool) allow_new_worlds = 1; +static _Atomic(bool) allow_new_worlds = true; JL_DLLEXPORT _Atomic(size_t) jl_world_counter = 1; // uses atomic acquire/release JL_DLLEXPORT size_t jl_get_world_counter(void) JL_NOTSAFEPOINT { @@ -1954,7 +1954,7 @@ JL_DLLEXPORT void jl_disable_new_worlds(void) { if (jl_generating_output()) jl_error("Disabling Method changes is not possible when generating output."); - jl_atomic_store_release(&allow_new_worlds, 0); + jl_atomic_store_release(&allow_new_worlds, false); jl_foreach_reachable_mtable(erase_all_backedges, (void*)NULL); }