diff --git a/crates/rb-sys-tests/src/stable_api_test.rs b/crates/rb-sys-tests/src/stable_api_test.rs index 8c3156ca..f50fe2c0 100644 --- a/crates/rb-sys-tests/src/stable_api_test.rs +++ b/crates/rb-sys-tests/src/stable_api_test.rs @@ -389,6 +389,55 @@ parity_test! ( expected: false ); +parity_test! ( + name: test_rb_gc_adjust_memory_usage, + func: gc_adjust_memory_usage, + data_factory: { + 64isize + } +); + +parity_test! ( + name: test_rb_gc_adjust_memory_usage_negative, + func: gc_adjust_memory_usage, + data_factory: { + -64isize + } +); + +#[rb_sys_test_helpers::ruby_test] +fn test_rb_gc_writebarrier() { + use rb_sys::stable_api; + let old = ruby_eval!("String.new") as VALUE; + let young = ruby_eval!("42") as VALUE; + assert_ne!(stable_api::get_default().version(), (0, 0)); + #[allow(unused)] + let rust_result = unsafe { stable_api::get_default().gc_writebarrier(old, young) }; + #[allow(unused_unsafe)] + let compiled_c_result = unsafe { stable_api::get_compiled().gc_writebarrier(old, young) }; + assert_eq!( + compiled_c_result, rust_result, + "compiled_c was {:?}, rust was {:?}", + compiled_c_result, rust_result + ); +} + +#[rb_sys_test_helpers::ruby_test] +fn test_rb_gc_writebarrier_unprotect() { + use rb_sys::stable_api; + let obj = ruby_eval!("String.new") as VALUE; + assert_ne!(stable_api::get_default().version(), (0, 0)); + #[allow(unused)] + let rust_result = unsafe { stable_api::get_default().gc_writebarrier_unprotect(obj) }; + #[allow(unused_unsafe)] + let compiled_c_result = unsafe { stable_api::get_compiled().gc_writebarrier_unprotect(obj) }; + assert_eq!( + compiled_c_result, rust_result, + "compiled_c was {:?}, rust was {:?}", + compiled_c_result, rust_result + ); +} + parity_test! ( name: test_rb_static_sym_p_for_static_sym, func: static_sym_p, diff --git a/crates/rb-sys/src/stable_api.rs b/crates/rb-sys/src/stable_api.rs index 10b34f9c..55eda450 100644 --- a/crates/rb-sys/src/stable_api.rs +++ b/crates/rb-sys/src/stable_api.rs @@ -112,6 +112,17 @@ pub trait StableApiDefinition { /// Checks if the given object is a so-called Fixnum. fn fixnum_p(&self, obj: VALUE) -> bool; + /// Informs that there are external memory usages, so the GC can run more + /// often than it otherwise would if it was unaware of such allocations. + fn gc_adjust_memory_usage(&self, diff: isize); + + /// Informs the GC that `young` is a new reference to `old`, allowing the + /// the old object to participate in generational GC. + fn gc_writebarrier(&self, old: VALUE, young: VALUE); + + /// Opts out of generational GC and write barrier protection. + fn gc_writebarrier_unprotect(&self, obj: VALUE); + /// Checks if the given object is a dynamic symbol. /// /// # Safety diff --git a/crates/rb-sys/src/stable_api/compiled.c b/crates/rb-sys/src/stable_api/compiled.c index db89dc1f..1e263951 100644 --- a/crates/rb-sys/src/stable_api/compiled.c +++ b/crates/rb-sys/src/stable_api/compiled.c @@ -61,6 +61,21 @@ impl_fixnum_p(VALUE obj) { return FIXNUM_P(obj); } +void +impl_gc_adjust_memory_usage(size_t diff) { + rb_gc_adjust_memory_usage(diff); +} + +void +impl_gc_writebarrier(VALUE old, VALUE young) { + rb_gc_writebarrier(old, young); +} + +void +impl_gc_writebarrier_unprotect(VALUE obj) { + rb_gc_writebarrier_unprotect(obj); +} + int impl_static_sym_p(VALUE obj) { return STATIC_SYM_P(obj); diff --git a/crates/rb-sys/src/stable_api/compiled.rs b/crates/rb-sys/src/stable_api/compiled.rs index 527fabd0..93c9c562 100644 --- a/crates/rb-sys/src/stable_api/compiled.rs +++ b/crates/rb-sys/src/stable_api/compiled.rs @@ -44,6 +44,15 @@ extern "C" { #[link_name = "impl_fixnum_p"] fn impl_fixnum_p(obj: VALUE) -> bool; + #[link_name = "impl_gc_adjust_memory_usage"] + fn impl_gc_adjust_memory_usage(diff: isize); + + #[link_name = "impl_gc_writebarrier"] + fn impl_gc_writebarrier(old: VALUE, young: VALUE); + + #[link_name = "impl_gc_writebarrier_unprotect"] + fn impl_gc_writebarrier_unprotect(obj: VALUE); + #[link_name = "impl_static_sym_p"] fn impl_static_sym_p(obj: VALUE) -> bool; @@ -146,6 +155,21 @@ impl StableApiDefinition for Definition { unsafe { impl_fixnum_p(obj) } } + #[inline] + fn gc_adjust_memory_usage(&self, diff: isize) { + unsafe { impl_gc_adjust_memory_usage(diff) } + } + + #[inline] + fn gc_writebarrier(&self, old: VALUE, young: VALUE) { + unsafe { impl_gc_writebarrier(old, young) } + } + + #[inline] + fn gc_writebarrier_unprotect(&self, obj: VALUE) { + unsafe { impl_gc_writebarrier_unprotect(obj) } + } + #[inline] fn static_sym_p(&self, obj: VALUE) -> bool { unsafe { impl_static_sym_p(obj) } diff --git a/crates/rb-sys/src/stable_api/ruby_2_6.rs b/crates/rb-sys/src/stable_api/ruby_2_6.rs index 0407f8d7..8a5801b0 100644 --- a/crates/rb-sys/src/stable_api/ruby_2_6.rs +++ b/crates/rb-sys/src/stable_api/ruby_2_6.rs @@ -142,6 +142,21 @@ impl StableApiDefinition for Definition { (obj & crate::FIXNUM_FLAG as VALUE) != 0 } + #[inline] + fn gc_adjust_memory_usage(&self, diff: isize) { + unsafe { crate::rb_gc_adjust_memory_usage(diff as _) }; + } + + #[inline] + fn gc_writebarrier(&self, old: VALUE, young: VALUE) { + unsafe { crate::rb_gc_writebarrier(old, young) } + } + + #[inline] + fn gc_writebarrier_unprotect(&self, obj: VALUE) { + unsafe { crate::rb_gc_writebarrier_unprotect(obj) } + } + #[inline] fn static_sym_p(&self, obj: VALUE) -> bool { let mask = !(VALUE::MAX << crate::ruby_special_consts::RUBY_SPECIAL_SHIFT as VALUE); diff --git a/crates/rb-sys/src/stable_api/ruby_2_7.rs b/crates/rb-sys/src/stable_api/ruby_2_7.rs index e5a1eb12..a2d0d444 100644 --- a/crates/rb-sys/src/stable_api/ruby_2_7.rs +++ b/crates/rb-sys/src/stable_api/ruby_2_7.rs @@ -142,6 +142,21 @@ impl StableApiDefinition for Definition { (obj & crate::FIXNUM_FLAG as VALUE) != 0 } + #[inline] + fn gc_adjust_memory_usage(&self, diff: isize) { + unsafe { crate::rb_gc_adjust_memory_usage(diff as _) }; + } + + #[inline] + fn gc_writebarrier(&self, old: VALUE, young: VALUE) { + unsafe { crate::rb_gc_writebarrier(old, young) } + } + + #[inline] + fn gc_writebarrier_unprotect(&self, obj: VALUE) { + unsafe { crate::rb_gc_writebarrier_unprotect(obj) } + } + #[inline] fn static_sym_p(&self, obj: VALUE) -> bool { let mask = !(VALUE::MAX << crate::ruby_special_consts::RUBY_SPECIAL_SHIFT as VALUE); diff --git a/crates/rb-sys/src/stable_api/ruby_3_0.rs b/crates/rb-sys/src/stable_api/ruby_3_0.rs index cd22ae7a..489445d6 100644 --- a/crates/rb-sys/src/stable_api/ruby_3_0.rs +++ b/crates/rb-sys/src/stable_api/ruby_3_0.rs @@ -150,6 +150,21 @@ impl StableApiDefinition for Definition { (obj & crate::FIXNUM_FLAG as VALUE) != 0 } + #[inline] + fn gc_adjust_memory_usage(&self, diff: isize) { + unsafe { crate::rb_gc_adjust_memory_usage(diff as _) }; + } + + #[inline] + fn gc_writebarrier(&self, old: VALUE, young: VALUE) { + unsafe { crate::rb_gc_writebarrier(old, young) } + } + + #[inline] + fn gc_writebarrier_unprotect(&self, obj: VALUE) { + unsafe { crate::rb_gc_writebarrier_unprotect(obj) } + } + #[inline] fn static_sym_p(&self, obj: VALUE) -> bool { let mask = !(VALUE::MAX << crate::ruby_special_consts::RUBY_SPECIAL_SHIFT as VALUE); diff --git a/crates/rb-sys/src/stable_api/ruby_3_1.rs b/crates/rb-sys/src/stable_api/ruby_3_1.rs index a604426f..eb7f5b99 100644 --- a/crates/rb-sys/src/stable_api/ruby_3_1.rs +++ b/crates/rb-sys/src/stable_api/ruby_3_1.rs @@ -143,6 +143,21 @@ impl StableApiDefinition for Definition { (obj & crate::FIXNUM_FLAG as VALUE) != 0 } + #[inline] + fn gc_adjust_memory_usage(&self, diff: isize) { + unsafe { crate::rb_gc_adjust_memory_usage(diff as _) }; + } + + #[inline] + fn gc_writebarrier(&self, old: VALUE, young: VALUE) { + unsafe { crate::rb_gc_writebarrier(old, young) } + } + + #[inline] + fn gc_writebarrier_unprotect(&self, obj: VALUE) { + unsafe { crate::rb_gc_writebarrier_unprotect(obj) } + } + #[inline] fn static_sym_p(&self, obj: VALUE) -> bool { let mask = !(VALUE::MAX << crate::ruby_special_consts::RUBY_SPECIAL_SHIFT as VALUE); diff --git a/crates/rb-sys/src/stable_api/ruby_3_2.rs b/crates/rb-sys/src/stable_api/ruby_3_2.rs index 52e0067e..afe5a823 100644 --- a/crates/rb-sys/src/stable_api/ruby_3_2.rs +++ b/crates/rb-sys/src/stable_api/ruby_3_2.rs @@ -137,6 +137,21 @@ impl StableApiDefinition for Definition { (obj & crate::FIXNUM_FLAG as VALUE) != 0 } + #[inline] + fn gc_adjust_memory_usage(&self, diff: isize) { + unsafe { crate::rb_gc_adjust_memory_usage(diff as _) }; + } + + #[inline] + fn gc_writebarrier(&self, old: VALUE, young: VALUE) { + unsafe { crate::rb_gc_writebarrier(old, young) } + } + + #[inline] + fn gc_writebarrier_unprotect(&self, obj: VALUE) { + unsafe { crate::rb_gc_writebarrier_unprotect(obj) } + } + #[inline] fn static_sym_p(&self, obj: VALUE) -> bool { let mask = !(VALUE::MAX << crate::ruby_special_consts::RUBY_SPECIAL_SHIFT as VALUE); diff --git a/crates/rb-sys/src/stable_api/ruby_3_3.rs b/crates/rb-sys/src/stable_api/ruby_3_3.rs index d068fac1..7620abc2 100644 --- a/crates/rb-sys/src/stable_api/ruby_3_3.rs +++ b/crates/rb-sys/src/stable_api/ruby_3_3.rs @@ -130,6 +130,21 @@ impl StableApiDefinition for Definition { (obj & crate::FIXNUM_FLAG as VALUE) != 0 } + #[inline] + fn gc_adjust_memory_usage(&self, diff: isize) { + unsafe { crate::rb_gc_adjust_memory_usage(diff as _) }; + } + + #[inline] + fn gc_writebarrier(&self, old: VALUE, young: VALUE) { + unsafe { crate::rb_gc_writebarrier(old, young) } + } + + #[inline] + fn gc_writebarrier_unprotect(&self, obj: VALUE) { + unsafe { crate::rb_gc_writebarrier_unprotect(obj) } + } + #[inline] fn static_sym_p(&self, obj: VALUE) -> bool { let mask = !(VALUE::MAX << crate::ruby_special_consts::RUBY_SPECIAL_SHIFT as VALUE); diff --git a/crates/rb-sys/src/stable_api/ruby_3_4.rs b/crates/rb-sys/src/stable_api/ruby_3_4.rs index 74a82f5b..b0a9355a 100644 --- a/crates/rb-sys/src/stable_api/ruby_3_4.rs +++ b/crates/rb-sys/src/stable_api/ruby_3_4.rs @@ -130,6 +130,21 @@ impl StableApiDefinition for Definition { (obj & crate::FIXNUM_FLAG as VALUE) != 0 } + #[inline] + fn gc_adjust_memory_usage(&self, diff: isize) { + unsafe { crate::rb_gc_adjust_memory_usage(diff as _) }; + } + + #[inline] + fn gc_writebarrier(&self, old: VALUE, young: VALUE) { + unsafe { crate::rb_gc_writebarrier(old, young) } + } + + #[inline] + fn gc_writebarrier_unprotect(&self, obj: VALUE) { + unsafe { crate::rb_gc_writebarrier_unprotect(obj) } + } + #[inline] fn static_sym_p(&self, obj: VALUE) -> bool { let mask = !(VALUE::MAX << crate::ruby_special_consts::RUBY_SPECIAL_SHIFT as VALUE);