Skip to content

Commit 170b8de

Browse files
committed
Weak field processing
We allow the C part to discover weak fields during tracing so that we will clear or forward them during the weak reference processing phase.
1 parent b6d3321 commit 170b8de

File tree

5 files changed

+107
-4
lines changed

5 files changed

+107
-4
lines changed

mmtk/Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ edition = "2021"
1212
# Metadata for the Ruby repository
1313
[package.metadata.ci-repos.ruby]
1414
repo = "mmtk/ruby" # This is used by actions/checkout, so the format is "owner/repo", not URL.
15-
rev = "64c07dcd7ca763e25607848df3be909135e02589"
15+
rev = "fe51e6277b4df810a90c4474f951ed0a263acea0"
1616

1717
[lib]
1818
name = "mmtk_ruby"

mmtk/cbindgen.toml

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,8 +8,9 @@ tab_width = 4
88

99
usize_is_size_t = true
1010

11-
after_includes = """
11+
includes = ["ruby/internal/value.h"]
1212

13+
after_includes = """
1314
typedef struct MMTk_Builder MMTk_Builder;
1415
typedef struct MMTk_Mutator MMTk_Mutator;
1516
@@ -26,6 +27,7 @@ typedef uint32_t MMTk_AllocationSemantics;
2627

2728
[export]
2829
include = ["HiddenHeader"]
30+
exclude = ["VALUE"]
2931

3032
[export.rename]
3133
"MMTKBuilder" = "MMTk_Builder"

mmtk/src/abi.rs

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,27 @@ pub const HIDDEN_SIZE_MASK: usize = 0x0000FFFFFFFFFFFF;
1515
pub const MMTK_WEAK_CONCURRENT_SET_KIND_FSTRING: u8 = 0;
1616
pub const MMTK_WEAK_CONCURRENT_SET_KIND_GLOBAL_SYMBOLS: u8 = 1;
1717

18+
pub(crate) const RUBY_IMMEDIATE_MASK: usize = 0x07;
19+
20+
#[allow(non_upper_case_globals)] // Match Ruby definition
21+
pub(crate) const Qundef: VALUE = VALUE(0x24);
22+
23+
#[repr(transparent)]
24+
#[derive(Clone, Copy, PartialEq, Eq, Debug)]
25+
pub struct VALUE(pub usize);
26+
27+
impl VALUE {
28+
pub fn is_special_const(&self) -> bool {
29+
self.0 == 0 || self.0 & RUBY_IMMEDIATE_MASK != 0
30+
}
31+
}
32+
33+
impl From<ObjectReference> for VALUE {
34+
fn from(value: ObjectReference) -> Self {
35+
Self(value.to_raw_address().as_usize())
36+
}
37+
}
38+
1839
// An opaque type for the C counterpart.
1940
#[allow(non_camel_case_types)]
2041
pub struct st_table;

mmtk/src/api.rs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ use crate::abi;
99
use crate::abi::HiddenHeader;
1010
use crate::abi::RawVecOfObjRef;
1111
use crate::abi::RubyBindingOptions;
12+
use crate::abi::VALUE;
1213
use crate::binding;
1314
use crate::binding::RubyBinding;
1415
use crate::mmtk;
@@ -421,3 +422,8 @@ pub extern "C" fn mmtk_current_gc_is_nursery() -> bool {
421422
.generational()
422423
.is_some_and(|gen| gen.is_current_gc_nursery())
423424
}
425+
426+
#[no_mangle]
427+
pub extern "C" fn mmtk_discover_weak_field(field: *mut VALUE) {
428+
crate::binding().weak_proc.discover_weak_field(field)
429+
}

mmtk/src/weak_proc.rs

Lines changed: 76 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,12 +2,12 @@ use std::sync::Mutex;
22

33
use mmtk::{
44
scheduler::{GCWork, GCWorker, WorkBucketStage},
5-
util::ObjectReference,
5+
util::{Address, ObjectReference},
66
vm::ObjectTracerContext,
77
};
88

99
use crate::{
10-
abi::{self, GCThreadTLS},
10+
abi::{self, GCThreadTLS, Qundef, VALUE},
1111
extra_assert, is_mmtk_object_safe, upcalls, Ruby,
1212
};
1313

@@ -27,11 +27,15 @@ pub enum WeakConcurrentSetKind {
2727
GlobalSymbols = abi::MMTK_WEAK_CONCURRENT_SET_KIND_GLOBAL_SYMBOLS,
2828
}
2929

30+
pub type FieldType = *mut VALUE;
31+
3032
pub struct WeakProcessor {
3133
/// Objects that needs `obj_free` called when dying.
3234
/// If it is a bottleneck, replace it with a lock-free data structure,
3335
/// or add candidates in batch.
3436
obj_free_candidates: Mutex<Vec<ObjectReference>>,
37+
/// Weak fields discovered during the current GC.
38+
weak_fields: Mutex<Vec<FieldType>>,
3539
}
3640

3741
impl Default for WeakProcessor {
@@ -44,6 +48,7 @@ impl WeakProcessor {
4448
pub fn new() -> Self {
4549
Self {
4650
obj_free_candidates: Mutex::new(Vec::new()),
51+
weak_fields: Default::default(),
4752
}
4853
}
4954

@@ -70,6 +75,28 @@ impl WeakProcessor {
7075
std::mem::take(obj_free_candidates.as_mut())
7176
}
7277

78+
pub fn clear_weak_fields(&self) {
79+
let mut weak_fields = self
80+
.weak_fields
81+
.try_lock()
82+
.expect("Should not have contention.");
83+
weak_fields.clear();
84+
}
85+
86+
pub fn discover_weak_field(&self, field: FieldType) {
87+
let mut weak_fields = self.weak_fields.lock().unwrap();
88+
weak_fields.push(field);
89+
trace!("Pushed weak field {field:?}");
90+
}
91+
92+
pub fn get_all_weak_fields(&self) -> Vec<FieldType> {
93+
let mut weak_fields = self
94+
.weak_fields
95+
.try_lock()
96+
.expect("Should not have contention.");
97+
std::mem::take(&mut weak_fields)
98+
}
99+
73100
pub fn process_weak_stuff(
74101
&self,
75102
worker: &mut GCWorker<Ruby>,
@@ -88,6 +115,7 @@ impl WeakProcessor {
88115
Box::new(UpdateCCRefinementTable) as _,
89116
// END: Weak tables
90117
Box::new(UpdateWbUnprotectedObjectsList) as _,
118+
Box::new(UpdateWeakFields) as _,
91119
]);
92120

93121
if SPECIALIZE_FSTRING_TABLE_PROCESSING {
@@ -317,3 +345,49 @@ impl Forwardable for ObjectReference {
317345
self.get_forwarded_object().unwrap_or(*self)
318346
}
319347
}
348+
349+
struct UpdateWeakFields;
350+
351+
impl GCWork<Ruby> for UpdateWeakFields {
352+
fn do_work(&mut self, _worker: &mut GCWorker<Ruby>, _mmtk: &'static mmtk::MMTK<Ruby>) {
353+
let weak_fields = crate::binding().weak_proc.get_all_weak_fields();
354+
355+
let num_fields = weak_fields.len();
356+
let mut live = 0usize;
357+
let mut forwarded = 0usize;
358+
359+
debug!("Updating {num_fields} weak fields...");
360+
361+
for field in weak_fields {
362+
let old_value = unsafe { *field };
363+
trace!(" Visiting weak field {field:?} -> {old_value:?}");
364+
365+
if old_value.is_special_const() {
366+
continue;
367+
}
368+
369+
let addr = unsafe { Address::from_usize(old_value.0) };
370+
if !addr.is_mapped() {
371+
panic!("Field {field:?} value {addr} points to unmapped area");
372+
}
373+
let Some(old_objref) = mmtk::memory_manager::is_mmtk_object(addr) else {
374+
panic!("Field {field:?} value {addr} is an invalid object reference");
375+
};
376+
377+
if old_objref.is_reachable() {
378+
live += 1;
379+
if let Some(new_objref) = old_objref.get_forwarded_object() {
380+
forwarded += 1;
381+
let new_value = VALUE::from(new_objref);
382+
trace!(" Updated weak field {field:?} to {new_value:?}");
383+
unsafe { *field = new_value };
384+
}
385+
} else {
386+
unsafe { *field = Qundef };
387+
}
388+
}
389+
390+
debug!("Updated {num_fields} weak fields. {live} live, {forwarded} forwarded.");
391+
probe!(mmtk_ruby, update_weak_fields, num_fields, live, forwarded);
392+
}
393+
}

0 commit comments

Comments
 (0)