From 2b153c62e50e0ba13a660e8bacc9ae350faa2b0b Mon Sep 17 00:00:00 2001 From: Yi Lin Date: Tue, 25 Jul 2023 16:18:32 +1200 Subject: [PATCH] Use Julia's finalizer implementation (#78) This PR updates to https://github.com/mmtk/julia/pull/22. * Remove code about registering and running finalizers (we use whatever Julia does). * Add code for finalizer scanning in Rust. --- .gitignore | 3 +- julia/mmtk_julia.c | 75 +++++-------- mmtk/Cargo.lock | 50 ++++----- mmtk/Cargo.toml | 6 +- mmtk/api/mmtk.h | 6 +- mmtk/src/api.rs | 112 +------------------ mmtk/src/collection.rs | 7 ++ mmtk/src/julia_finalizer.rs | 214 ++++++++++++++++++++++++++++++++++++ mmtk/src/lib.rs | 17 +-- mmtk/src/scanning.rs | 43 +++++--- 10 files changed, 321 insertions(+), 212 deletions(-) create mode 100644 mmtk/src/julia_finalizer.rs diff --git a/.gitignore b/.gitignore index 2c56ae94..3b9f4dee 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,4 @@ .idea/ julia/*.o -.vscode +julia/*.dbj.obj +.vscode \ No newline at end of file diff --git a/julia/mmtk_julia.c b/julia/mmtk_julia.c index 8062f7d8..8f5a548f 100644 --- a/julia/mmtk_julia.c +++ b/julia/mmtk_julia.c @@ -346,61 +346,39 @@ size_t get_so_size(void* obj_raw) return 0; } -void run_finalizer_function(void *o_raw, void *ff_raw, bool is_ptr) -{ - jl_value_t *o = (jl_value_t*) o_raw; - jl_value_t *ff = (jl_value_t*) ff_raw; - if (is_ptr) { - run_finalizer(jl_current_task, (jl_value_t *)(((uintptr_t)o) | 1), (jl_value_t *)ff); - } else { - run_finalizer(jl_current_task, (jl_value_t *) o, (jl_value_t *)ff); +extern void run_finalizers(jl_task_t *ct); + +// Called after GC to run finalizers +void mmtk_jl_run_finalizers(void* ptls_raw) { + jl_ptls_t ptls = (jl_ptls_t) ptls_raw; + if (!ptls->finalizers_inhibited && ptls->locks.len == 0) { + JL_TIMING(GC, GC_Finalizers); + run_finalizers(jl_current_task); } } +// We implement finalization in the binding side. These functions +// returns some pointers so MMTk can manipulate finalizer lists. + +extern jl_mutex_t finalizers_lock; +extern arraylist_t to_finalize; +extern arraylist_t finalizer_list_marked; -static inline void mmtk_jl_run_finalizers_in_list(bool at_exit) { - jl_task_t* ct = jl_current_task; - uint8_t sticky = ct->sticky; - mmtk_run_finalizers(at_exit); - ct->sticky = sticky; +void* get_thread_finalizer_list(void* ptls_raw) { + jl_ptls_t ptls = (jl_ptls_t) ptls_raw; + return (void*)&ptls->finalizers; } -void mmtk_jl_run_pending_finalizers(void* ptls) { - if (!((jl_ptls_t)ptls)->in_finalizer && !((jl_ptls_t)ptls)->finalizers_inhibited && ((jl_ptls_t)ptls)->locks.len == 0) { - jl_task_t *ct = jl_current_task; - ((jl_ptls_t)ptls)->in_finalizer = 1; - uint64_t save_rngState[JL_RNG_SIZE]; - memcpy(&save_rngState[0], &ct->rngState[0], sizeof(save_rngState)); - jl_rng_split(ct->rngState, finalizer_rngState); - jl_atomic_store_relaxed(&jl_gc_have_pending_finalizers, 0); - mmtk_jl_run_finalizers_in_list(false); - memcpy(&ct->rngState[0], &save_rngState[0], sizeof(save_rngState)); - ((jl_ptls_t)ptls)->in_finalizer = 0; - } +void* get_to_finalize_list(void) { + return (void*)&to_finalize; } -void mmtk_jl_run_finalizers(void* ptls) { - // Only disable finalizers on current thread - // Doing this on all threads is racy (it's impossible to check - // or wait for finalizers on other threads without dead lock). - if (!((jl_ptls_t)ptls)->finalizers_inhibited && ((jl_ptls_t)ptls)->locks.len == 0) { - jl_task_t *ct = jl_current_task; - int8_t was_in_finalizer = ((jl_ptls_t)ptls)->in_finalizer; - ((jl_ptls_t)ptls)->in_finalizer = 1; - uint64_t save_rngState[JL_RNG_SIZE]; - memcpy(&save_rngState[0], &ct->rngState[0], sizeof(save_rngState)); - jl_rng_split(ct->rngState, finalizer_rngState); - jl_atomic_store_relaxed(&jl_gc_have_pending_finalizers, 0); - mmtk_jl_run_finalizers_in_list(false); - memcpy(&ct->rngState[0], &save_rngState[0], sizeof(save_rngState)); - ((jl_ptls_t)ptls)->in_finalizer = was_in_finalizer; - } else { - jl_atomic_store_relaxed(&jl_gc_have_pending_finalizers, 1); - } +void* get_marked_finalizers_list(void) { + return (void*)&finalizer_list_marked; } -void mmtk_jl_gc_run_all_finalizers(void) { - mmtk_jl_run_finalizers_in_list(true); +int* get_jl_gc_have_pending_finalizers(void) { + return (int*)&jl_gc_have_pending_finalizers; } // add the initial root set to mmtk roots @@ -928,7 +906,7 @@ Julia_Upcalls mmtk_upcalls = (Julia_Upcalls) { .scan_julia_exc_obj = scan_julia_exc_obj, .get_stackbase = get_stackbase, .calculate_roots = calculate_roots, - .run_finalizer_function = run_finalizer_function, + // .run_finalizer_function = run_finalizer_function, .get_jl_last_err = get_jl_last_err, .set_jl_last_err = set_jl_last_err, .get_lo_size = get_lo_size, @@ -946,4 +924,9 @@ Julia_Upcalls mmtk_upcalls = (Julia_Upcalls) { .jl_hrtime = jl_hrtime, .update_gc_time = update_gc_time, .get_abi_structs_checksum_c = get_abi_structs_checksum_c, + .get_thread_finalizer_list = get_thread_finalizer_list, + .get_to_finalize_list = get_to_finalize_list, + .get_marked_finalizers_list = get_marked_finalizers_list, + .arraylist_grow = (void (*)(void*, long unsigned int))arraylist_grow, + .get_jl_gc_have_pending_finalizers = get_jl_gc_have_pending_finalizers, }; diff --git a/mmtk/Cargo.lock b/mmtk/Cargo.lock index 121dd62d..14e94783 100644 --- a/mmtk/Cargo.lock +++ b/mmtk/Cargo.lock @@ -216,9 +216,9 @@ checksum = "9ea835d29036a4087793836fa931b08837ad5e957da9e23886b29586fb9b6650" [[package]] name = "either" -version = "1.8.1" +version = "1.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7fcaabb2fef8c910e7f4c7ce9f67a1283a1715879a7c230ca9d6d1ae31f16d91" +checksum = "a26ae43d7bcc3b814de94796a5e736d4029efb0ee900c12e2d54c993ad1a1e07" [[package]] name = "enum-map" @@ -237,7 +237,7 @@ checksum = "8560b409800a72d2d7860f8e5f4e0b0bd22bea6a352ea2a9ce30ccdef7f16d2f" dependencies = [ "proc-macro2", "quote", - "syn 2.0.26", + "syn 2.0.27", ] [[package]] @@ -423,9 +423,9 @@ dependencies = [ [[package]] name = "libz-sys" -version = "1.1.9" +version = "1.1.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "56ee889ecc9568871456d42f603d6a0ce59ff328d291063a45cbdf0036baf6db" +checksum = "24e6ab01971eb092ffe6a7d42f49f9ff42662f17604681e2843ad65077ba47dc" dependencies = [ "cc", "libc", @@ -538,9 +538,9 @@ dependencies = [ [[package]] name = "num-traits" -version = "0.2.15" +version = "0.2.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "578ede34cf02f8924ab9447f50c28075b4d3e5b269972345e7e0372b38c6cdcd" +checksum = "f30b0abd723be7e2ffca1272140fac1a2f084c77ec3e123c192b66af1ee9e6c2" dependencies = [ "autocfg", ] @@ -618,9 +618,9 @@ dependencies = [ [[package]] name = "quote" -version = "1.0.31" +version = "1.0.32" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5fe8a65d69dd0808184ebb5f836ab526bb259db23c657efa38711b1072ee47f0" +checksum = "50f3b39ccfb720540debaa0164757101c08ecb8d326b15358ce76a62c7e85965" dependencies = [ "proc-macro2", ] @@ -740,22 +740,22 @@ dependencies = [ [[package]] name = "serde" -version = "1.0.173" +version = "1.0.175" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e91f70896d6720bc714a4a57d22fc91f1db634680e65c8efe13323f1fa38d53f" +checksum = "5d25439cd7397d044e2748a6fe2432b5e85db703d6d097bd014b3c0ad1ebff0b" dependencies = [ "serde_derive", ] [[package]] name = "serde_derive" -version = "1.0.173" +version = "1.0.175" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a6250dde8342e0232232be9ca3db7aa40aceb5a3e5dd9bddbc00d99a007cde49" +checksum = "b23f7ade6f110613c0d63858ddb8b94c1041f550eab58a16b371bdf2c9c80ab4" dependencies = [ "proc-macro2", "quote", - "syn 2.0.26", + "syn 2.0.27", ] [[package]] @@ -805,9 +805,9 @@ dependencies = [ [[package]] name = "syn" -version = "2.0.26" +version = "2.0.27" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "45c3457aacde3c65315de5031ec191ce46604304d2446e803d71ade03308d970" +checksum = "b60f673f44a8255b9c8c657daf66a596d435f2da81a555b06dc644d080ba45e0" dependencies = [ "proc-macro2", "quote", @@ -816,9 +816,9 @@ dependencies = [ [[package]] name = "sysinfo" -version = "0.29.5" +version = "0.29.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6b949f01f9c23823744b71e0060472ecbde578ef68cc2a9e46d114efd77c3034" +checksum = "c7cb97a5a85a136d84e75d5c3cf89655090602efb1be0d8d5337b7e386af2908" dependencies = [ "cfg-if", "core-foundation-sys", @@ -840,22 +840,22 @@ dependencies = [ [[package]] name = "thiserror" -version = "1.0.43" +version = "1.0.44" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a35fc5b8971143ca348fa6df4f024d4d55264f3468c71ad1c2f365b0a4d58c42" +checksum = "611040a08a0439f8248d1990b111c95baa9c704c805fa1f62104b39655fd7f90" dependencies = [ "thiserror-impl", ] [[package]] name = "thiserror-impl" -version = "1.0.43" +version = "1.0.44" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "463fe12d7993d3b327787537ce8dd4dfa058de32fc2b195ef3cde03dc4771e8f" +checksum = "090198534930841fab3a5d1bb637cde49e339654e606195f8d9c76eeb081dc96" dependencies = [ "proc-macro2", "quote", - "syn 2.0.26", + "syn 2.0.27", ] [[package]] @@ -970,7 +970,7 @@ dependencies = [ "once_cell", "proc-macro2", "quote", - "syn 2.0.26", + "syn 2.0.27", "wasm-bindgen-shared", ] @@ -992,7 +992,7 @@ checksum = "54681b18a46765f095758388f2d0cf16eb8d4169b639ab575a8f5693af210c7b" dependencies = [ "proc-macro2", "quote", - "syn 2.0.26", + "syn 2.0.27", "wasm-bindgen-backend", "wasm-bindgen-shared", ] diff --git a/mmtk/Cargo.toml b/mmtk/Cargo.toml index 9e7a9557..cd0b1631 100644 --- a/mmtk/Cargo.toml +++ b/mmtk/Cargo.toml @@ -10,15 +10,17 @@ edition = "2018" [package.metadata.julia] # Our CI matches the following line and extract mmtk/julia. If this line is updated, please check ci yaml files and make sure it works. julia_repo = "https://github.com/mmtk/julia.git" -julia_version = "81bda9cc0c936ba1ec7bbfee5e6a05bee7b45b2d" +julia_version = "f690aa3a5621bfa1d6a07f911818f203d3f8d650" [lib] -crate-type = ["staticlib", "rlib", "dylib"] +crate-type = ["cdylib"] [build-dependencies] cc = "*" built = "*" +[profile.release] +lto = true [dependencies] libc = "0.2" diff --git a/mmtk/api/mmtk.h b/mmtk/api/mmtk.h index 445a3f66..8af7215e 100644 --- a/mmtk/api/mmtk.h +++ b/mmtk/api/mmtk.h @@ -72,7 +72,6 @@ typedef struct { void (* scan_julia_exc_obj) (void* obj, closure_pointer closure, ProcessEdgeFn process_edge); void* (* get_stackbase) (int16_t tid); void (* calculate_roots) (void* tls); - void (* run_finalizer_function) (void* obj, void* function, bool is_ptr); int (* get_jl_last_err) (void); void (* set_jl_last_err) (int e); size_t (* get_lo_size) (void* obj); @@ -90,6 +89,11 @@ typedef struct { uint64_t (* jl_hrtime) (void); void (* update_gc_time) (uint64_t); uintptr_t (* get_abi_structs_checksum_c) (void); + void* (* get_thread_finalizer_list) (void* tls); + void* (* get_to_finalize_list)(void); + void* (* get_marked_finalizers_list)(void); + void (*arraylist_grow)(void* a, size_t n); + int* (*get_jl_gc_have_pending_finalizers)(void); } Julia_Upcalls; /** diff --git a/mmtk/src/api.rs b/mmtk/src/api.rs index a83942e1..bf5d1e90 100644 --- a/mmtk/src/api.rs +++ b/mmtk/src/api.rs @@ -1,18 +1,13 @@ // All functions here are extern function. There is no point for marking them as unsafe. #![allow(clippy::not_unsafe_ptr_arg_deref)] -use crate::reference_glue::JuliaFinalizableObject; use crate::JuliaVM; use crate::Julia_Upcalls; use crate::BLOCK_FOR_GC; -use crate::FINALIZER_ROOTS; use crate::JULIA_HEADER_SIZE; use crate::SINGLETON; use crate::UPCALLS; -use crate::{ - set_julia_obj_header_size, BUILDER, DISABLED_GC, FINALIZERS_RUNNING, MUTATORS, - USER_TRIGGERED_GC, -}; +use crate::{set_julia_obj_header_size, BUILDER, DISABLED_GC, MUTATORS, USER_TRIGGERED_GC}; use crate::{ROOT_EDGES, ROOT_NODES}; use libc::c_char; @@ -321,51 +316,6 @@ pub extern "C" fn mmtk_harness_end(_tls: OpaquePointer) { memory_manager::harness_end(&SINGLETON) } -#[no_mangle] -pub extern "C" fn mmtk_register_finalizer( - obj: ObjectReference, - finalizer_fn: Address, - is_obj_ptr: bool, -) { - memory_manager::add_finalizer( - &SINGLETON, - JuliaFinalizableObject(obj, finalizer_fn, is_obj_ptr), - ); -} - -#[no_mangle] -pub extern "C" fn mmtk_run_finalizers_for_obj(obj: ObjectReference) { - let finalizers_running = AtomicBool::load(&FINALIZERS_RUNNING, Ordering::SeqCst); - - if !finalizers_running { - AtomicBool::store(&FINALIZERS_RUNNING, true, Ordering::SeqCst); - } - - let finalizable_objs = memory_manager::get_finalizers_for(&SINGLETON, obj); - - for obj in finalizable_objs.iter() { - // if the finalizer function triggers GC you don't want the object to be GC-ed - { - let mut fin_roots = FINALIZER_ROOTS.write().unwrap(); - let inserted = fin_roots.insert(*obj); - assert!(inserted); - } - } - - for obj in finalizable_objs { - unsafe { ((*UPCALLS).run_finalizer_function)(obj.0, obj.1, obj.2) } - { - let mut fin_roots = FINALIZER_ROOTS.write().unwrap(); - let removed = fin_roots.remove(&obj); - assert!(removed); - } - } - - if !finalizers_running { - AtomicBool::store(&FINALIZERS_RUNNING, false, Ordering::SeqCst); - } -} - #[no_mangle] pub extern "C" fn mmtk_process(name: *const c_char, value: *const c_char) -> bool { let name_str: &CStr = unsafe { CStr::from_ptr(name) }; @@ -510,66 +460,6 @@ pub static MMTK_NEEDS_WRITE_BARRIER: u8 = 0; #[cfg(feature = "stickyimmix")] pub static MMTK_NEEDS_WRITE_BARRIER: u8 = 1; -#[no_mangle] -pub extern "C" fn mmtk_run_finalizers(at_exit: bool) { - AtomicBool::store(&FINALIZERS_RUNNING, true, Ordering::SeqCst); - - if at_exit { - let mut all_finalizable = memory_manager::get_all_finalizers(&SINGLETON); - - { - // if the finalizer function triggers GC you don't want the objects to be GC-ed - let mut fin_roots = FINALIZER_ROOTS.write().unwrap(); - - for obj in all_finalizable.iter() { - let inserted = fin_roots.insert(*obj); - assert!(inserted); - } - } - - loop { - let to_be_finalized = all_finalizable.pop(); - - match to_be_finalized { - Some(obj) => unsafe { - ((*UPCALLS).run_finalizer_function)(obj.0, obj.1, obj.2); - { - let mut fin_roots = FINALIZER_ROOTS.write().unwrap(); - let removed = fin_roots.remove(&obj); - assert!(removed); - } - }, - None => break, - } - } - } else { - loop { - let to_be_finalized = memory_manager::get_finalized_object(&SINGLETON); - - match to_be_finalized { - Some(obj) => { - { - // if the finalizer function triggers GC you don't want the objects to be GC-ed - let mut fin_roots = FINALIZER_ROOTS.write().unwrap(); - - let inserted = fin_roots.insert(obj); - assert!(inserted); - } - unsafe { ((*UPCALLS).run_finalizer_function)(obj.0, obj.1, obj.2) } - { - let mut fin_roots = FINALIZER_ROOTS.write().unwrap(); - let removed = fin_roots.remove(&obj); - assert!(removed); - } - } - None => break, - } - } - } - - AtomicBool::store(&FINALIZERS_RUNNING, false, Ordering::SeqCst); -} - #[no_mangle] pub extern "C" fn mmtk_object_is_managed_by_mmtk(addr: usize) -> bool { crate::api::mmtk_is_mapped_address(unsafe { Address::from_usize(addr) }) diff --git a/mmtk/src/collection.rs b/mmtk/src/collection.rs index c8ed49f0..0a301557 100644 --- a/mmtk/src/collection.rs +++ b/mmtk/src/collection.rs @@ -144,3 +144,10 @@ impl Collection for VMCollection { crate::api::JULIA_MALLOC_BYTES.load(Ordering::SeqCst) } } + +pub fn is_current_gc_nursery() -> bool { + match crate::SINGLETON.get_plan().generational() { + Some(gen) => gen.is_current_gc_nursery(), + None => false, + } +} diff --git a/mmtk/src/julia_finalizer.rs b/mmtk/src/julia_finalizer.rs new file mode 100644 index 00000000..754fe81d --- /dev/null +++ b/mmtk/src/julia_finalizer.rs @@ -0,0 +1,214 @@ +use crate::UPCALLS; +use mmtk::memory_manager; +use mmtk::util::Address; +use mmtk::util::ObjectReference; +use mmtk::vm::ObjectTracer; +use mmtk::vm::VMBinding; +use mmtk::Mutator; + +use crate::JuliaVM; + +/// This is a Rust implementation of finalizer scanning in _jl_gc_collect() in gc.c +pub fn scan_finalizers_in_rust(tracer: &mut T) { + use crate::mmtk::vm::ActivePlan; + let to_finalize = ArrayListT::to_finalize_list(); + let marked_finalizers_list = ArrayListT::marked_finalizers_list(); + let jl_gc_have_pending_finalizers: *mut i32 = + unsafe { ((*UPCALLS).get_jl_gc_have_pending_finalizers)() }; + + // Current length of marked list: we only need to trace objects after this length if this is a nursery GC. + let mut orig_marked_len = marked_finalizers_list.len; + + // Sweep thread local list: if they are not alive, move to to_finalize. + for mutator in ::VMActivePlan::mutators() { + let list = ArrayListT::thread_local_finalizer_list(mutator); + sweep_finalizer_list( + list, + to_finalize, + Some(marked_finalizers_list), + jl_gc_have_pending_finalizers, + ); + } + + // If this is a full heap GC, we also sweep marked list. + if !crate::collection::is_current_gc_nursery() { + sweep_finalizer_list( + marked_finalizers_list, + to_finalize, + None, + jl_gc_have_pending_finalizers, + ); + orig_marked_len = 0; + } + + // Go through thread local list again and trace objects + for mutator in ::VMActivePlan::mutators() { + let list = ArrayListT::thread_local_finalizer_list(mutator); + mark_finlist(list, 0, tracer); + } + // Trace new objects in marked list + mark_finlist(marked_finalizers_list, orig_marked_len, tracer); + // Trace objects in to_finalize (which are just pushed in sweeping thread local list) + mark_finlist(to_finalize, 0, tracer); +} + +/// This maps to arraylist_t in arraylist.h. Defining the type allows us to access the list in Rust. +/// typedef struct { +/// size_t len; +/// size_t max; +/// void **items; +/// void *_space[AL_N_INLINE]; +/// } arraylist_t; +#[repr(C)] +struct ArrayListT { + len: usize, + max: usize, + items: *mut Address, + // There are one more field in the end but we dont use it. So omit it. +} + +impl ArrayListT { + // Some arraylist_t pointers used in finalizer implementation. + + /// ptls->finalizers: new finalizers are registered into this thread local list + fn thread_local_finalizer_list(mutator: &Mutator) -> &mut ArrayListT { + let list = unsafe { ((*UPCALLS).get_thread_finalizer_list)(mutator.mutator_tls.0 .0) }; + unsafe { &mut *list.to_mut_ptr() } + } + /// to_finalize: objects that are dead are in this list waiting for finalization + fn to_finalize_list<'a>() -> &'a mut ArrayListT { + let list = unsafe { ((*UPCALLS).get_to_finalize_list)() }; + unsafe { &mut *list.to_mut_ptr() } + } + /// finalizer_list_marked: objects that are alive and traced, thus we do not need to scan them again in future nursery GCs. + fn marked_finalizers_list<'a>() -> &'a mut ArrayListT { + let list = unsafe { ((*UPCALLS).get_marked_finalizers_list)() }; + unsafe { &mut *list.to_mut_ptr() } + } + + fn get(&self, i: usize) -> Address { + debug_assert!(i < self.len); + unsafe { *self.items.add(i) } + } + fn set(&mut self, i: usize, val: Address) { + debug_assert!(i < self.len); + unsafe { *self.items.add(i) = val } + } + fn push(&mut self, val: Address) { + self.grow(1); + self.set(self.len - 1, val); + } + fn grow(&mut self, n: usize) { + let newlen = self.len + n; + if newlen > self.max { + // Call into C to grow the list. + unsafe { + ((*UPCALLS).arraylist_grow)(Address::from_mut_ptr(self as _), n); + } + } + self.len = newlen + } +} + +fn gc_ptr_clear_tag(addr: Address, tag: usize) -> ObjectReference { + ObjectReference::from_raw_address(unsafe { Address::from_usize(addr & !tag) }) +} + +fn gc_ptr_tag(addr: Address, tag: usize) -> bool { + addr & tag != 0 +} + +// sweep_finalizer_list in gc.c +fn sweep_finalizer_list( + list: &mut ArrayListT, + to_finalize: &mut ArrayListT, + // finalizer_list_marked is None if list (1st parameter) is finalizer_list_marked. + // Rust does not allow sending the same mutable reference as two different arguments (cannot borrow __ as mutable more than once at a time) + mut finalizer_list_marked: Option<&mut ArrayListT>, + jl_gc_have_pending_finalizers: *mut i32, +) { + if list.len == 0 { + return; + } + + let mut i = 0; + let mut j = 0; + while i < list.len { + let v0: Address = list.get(i); + let v = gc_ptr_clear_tag(v0, 3); + if v0.is_zero() { + i += 2; + // remove from this list + continue; + } + + let fin = list.get(i + 1); + let (isfreed, isold) = if gc_ptr_tag(v0, 2) { + (true, false) + } else { + let isfreed = !memory_manager::is_live_object(v); + let isold = finalizer_list_marked.is_some() && !isfreed; + (isfreed, isold) + }; + if isfreed || isold { + // remove from this list + } else { + if j < i { + list.set(j, list.get(i)); + list.set(j + 1, list.get(i + 1)); + } + j += 2; + } + if isfreed { + to_finalize.push(v0); + to_finalize.push(fin); + unsafe { + *jl_gc_have_pending_finalizers = 1; + } + } + if isold { + let finalizer_list_marked = finalizer_list_marked.as_mut().unwrap(); + finalizer_list_marked.push(v0); + finalizer_list_marked.push(fin); + } + i += 2; + } + + list.len = j; +} + +// gc_mark_finlist in gc.c +fn mark_finlist(list: &mut ArrayListT, start: usize, tracer: &mut T) { + if list.len <= start { + return; + } + + let mut i = start; + while i < list.len { + let cur = list.get(i); + if cur.is_zero() { + i += 1; + continue; + } + + let new_obj = if gc_ptr_tag(cur, 1) { + // Skip next + i += 1; + debug_assert!(i < list.len); + gc_ptr_clear_tag(cur, 1) + } else { + ObjectReference::from_raw_address(cur) + }; + if gc_ptr_tag(cur, 2) { + i += 1; + continue; + } + + let traced = tracer.trace_object(new_obj); + debug_assert_eq!( + traced, new_obj, + "Object is moved -- we need to save the new object back to the finalizer list" + ); + i += 1; + } +} diff --git a/mmtk/src/lib.rs b/mmtk/src/lib.rs index ac0e423f..df57cef8 100644 --- a/mmtk/src/lib.rs +++ b/mmtk/src/lib.rs @@ -13,7 +13,6 @@ use mmtk::vm::VMBinding; use mmtk::MMTKBuilder; use mmtk::Mutator; use mmtk::MMTK; -use reference_glue::JuliaFinalizableObject; use std::collections::HashMap; use std::collections::HashSet; @@ -30,6 +29,7 @@ pub mod reference_glue; pub mod scanning; pub mod util; +pub mod julia_finalizer; pub mod julia_scanning; #[allow(non_camel_case_types)] #[allow(improper_ctypes_definitions)] @@ -79,12 +79,6 @@ pub static BLOCK_FOR_GC: AtomicBool = AtomicBool::new(false); #[no_mangle] pub static WORLD_HAS_STOPPED: AtomicBool = AtomicBool::new(false); -#[no_mangle] -pub static SCHEDULED_FINALIZATION: AtomicBool = AtomicBool::new(false); - -#[no_mangle] -pub static FINALIZERS_RUNNING: AtomicBool = AtomicBool::new(false); - #[no_mangle] pub static DISABLED_GC: AtomicBool = AtomicBool::new(false); @@ -98,8 +92,6 @@ lazy_static! { Arc::new((Mutex::new(0), Condvar::new())); pub static ref ROOT_NODES: Mutex> = Mutex::new(HashSet::new()); pub static ref ROOT_EDGES: Mutex> = Mutex::new(HashSet::new()); - pub static ref FINALIZER_ROOTS: RwLock> = - RwLock::new(HashSet::new()); // We create a boxed mutator with MMTk core, and we mem copy its content to jl_tls_state_t (shallow copy). // This map stores the pair of the mutator address in jl_tls_state_t and the original boxed mutator. @@ -140,8 +132,6 @@ pub struct Julia_Upcalls { ), pub get_stackbase: extern "C" fn(tid: u16) -> usize, pub calculate_roots: extern "C" fn(tls: OpaquePointer), - pub run_finalizer_function: - extern "C" fn(obj: ObjectReference, function: Address, is_ptr: bool), pub get_jl_last_err: extern "C" fn() -> u32, pub set_jl_last_err: extern "C" fn(errno: u32), pub get_lo_size: extern "C" fn(object: ObjectReference) -> usize, @@ -159,6 +149,11 @@ pub struct Julia_Upcalls { pub jl_hrtime: extern "C" fn() -> u64, pub update_gc_time: extern "C" fn(u64), pub get_abi_structs_checksum_c: extern "C" fn() -> usize, + pub get_thread_finalizer_list: extern "C" fn(tls: OpaquePointer) -> Address, + pub get_to_finalize_list: extern "C" fn() -> Address, + pub get_marked_finalizers_list: extern "C" fn() -> Address, + pub arraylist_grow: extern "C" fn(Address, usize), + pub get_jl_gc_have_pending_finalizers: extern "C" fn() -> *mut i32, } pub static mut UPCALLS: *const Julia_Upcalls = null_mut(); diff --git a/mmtk/src/scanning.rs b/mmtk/src/scanning.rs index 7640ad87..de3ad9c9 100644 --- a/mmtk/src/scanning.rs +++ b/mmtk/src/scanning.rs @@ -3,13 +3,13 @@ use crate::edges::JuliaVMEdge; use crate::julia_scanning::process_edge; #[cfg(feature = "scan_obj_c")] use crate::julia_scanning::process_offset_edge; -use crate::FINALIZER_ROOTS; use crate::{ROOT_EDGES, ROOT_NODES, SINGLETON, UPCALLS}; use mmtk::memory_manager; use mmtk::scheduler::*; use mmtk::util::opaque_pointer::*; use mmtk::util::ObjectReference; use mmtk::vm::EdgeVisitor; +use mmtk::vm::ObjectTracerContext; use mmtk::vm::RootsWorkFactory; use mmtk::vm::Scanning; use mmtk::vm::VMBinding; @@ -50,19 +50,6 @@ impl Scanning for VMScanning { for obj in roots.drain() { roots_to_scan.push(obj); } - - let fin_roots = FINALIZER_ROOTS.read().unwrap(); - - // processing finalizer roots - for obj in fin_roots.iter() { - if !obj.2 { - // not a void pointer - let obj_ref = ObjectReference::from_raw_address((*obj).1); - roots_to_scan.push(obj_ref); - } - roots_to_scan.push((*obj).0); - } - factory.create_process_node_roots_work(roots_to_scan); let roots: Vec = ROOT_EDGES @@ -83,7 +70,6 @@ impl Scanning for VMScanning { process_object(object, edge_visitor); } fn notify_initial_thread_scan_complete(_partial_scan: bool, _tls: VMWorkerThread) { - // Specific to JikesRVM - using it to load the work for sweeping malloced arrays let sweep_malloced_arrays_work = SweepMallocedArrays::new(); memory_manager::add_work_packet( &SINGLETON, @@ -98,6 +84,21 @@ impl Scanning for VMScanning { fn prepare_for_roots_re_scanning() { unimplemented!() } + + fn process_weak_refs( + _worker: &mut GCWorker, + tracer_context: impl ObjectTracerContext, + ) -> bool { + let single_thread_process_finalizer = ScanFinalizersSingleThreaded { tracer_context }; + memory_manager::add_work_packet( + &SINGLETON, + WorkBucketStage::VMRefClosure, + single_thread_process_finalizer, + ); + + // We have pushed work. No need to repeat this method. + false + } } pub fn process_object(object: ObjectReference, closure: &mut dyn EdgeVisitor) { @@ -134,3 +135,15 @@ impl GCWork for SweepMallocedArrays { self.swept = true; } } + +pub struct ScanFinalizersSingleThreaded> { + tracer_context: C, +} + +impl> GCWork for ScanFinalizersSingleThreaded { + fn do_work(&mut self, worker: &mut GCWorker, _mmtk: &'static MMTK) { + self.tracer_context.with_tracer(worker, |tracer| { + crate::julia_finalizer::scan_finalizers_in_rust(tracer); + }); + } +}