1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202
//! Functions for working with Ruby's Garbage Collector.
use std::ops::Deref;
use rb_sys::{
rb_gc_adjust_memory_usage, rb_gc_count, rb_gc_disable, rb_gc_enable, rb_gc_mark,
rb_gc_mark_locations, rb_gc_register_address, rb_gc_register_mark_object, rb_gc_start,
rb_gc_stat, rb_gc_unregister_address, VALUE,
};
#[cfg(ruby_gte_2_7)]
use rb_sys::{rb_gc_location, rb_gc_mark_movable};
use crate::{
error::{protect, Error},
r_hash::RHash,
symbol::Symbol,
value::{ReprValue, Value, QNIL},
};
#[cfg(target_pointer_width = "64")]
type DiffSize = i64;
#[cfg(target_pointer_width = "32")]
type DiffSize = i32;
/// Mark an Object.
///
/// Used to mark any stored Ruby objects when implementing
/// [`DataTypeFunctions::mark`](`crate::r_typed_data::DataTypeFunctions::mark`).
pub fn mark<T>(value: T)
where
T: Deref<Target = Value>,
{
unsafe { rb_gc_mark(value.as_rb_value()) };
}
/// Mark multiple Objects.
///
/// Used to mark any stored Ruby objects when implementing
/// [`DataTypeFunctions::mark`](`crate::r_typed_data::DataTypeFunctions::mark`).
pub fn mark_slice<T>(values: &[T])
where
T: ReprValue,
{
if let (Some(start), Some(end)) = (values.first(), values.last()) {
unsafe {
rb_gc_mark_locations(
start as *const _ as *const VALUE,
end as *const _ as *const VALUE,
)
}
};
}
/// Mark an Object and let Ruby know it is moveable.
///
/// The [`Value`] type is effectly a pointer to a Ruby object. Ruby's garbage
/// collector will avoid moving objects exposed to extensions, unless you use
/// this function to mark them during the GC marking phase.
///
/// Used to mark any stored Ruby objects when implementing
/// [`DataTypeFunctions::mark`](`crate::r_typed_data::DataTypeFunctions::mark`)
/// and you have also implemented
/// [`DataTypeFunctions::compact`](`crate::r_typed_data::DataTypeFunctions::compact`).
///
/// Beware that any Ruby object passed to this function may later become
/// invalid to use from Rust when GC is run, you must update any stored objects
/// with [`location`] inside your implementation of
/// [`DataTypeFunctions::compact`](`crate::r_typed_data::DataTypeFunctions::compact`).
#[cfg(any(ruby_gte_2_7, docsrs))]
#[cfg_attr(docsrs, doc(cfg(ruby_gte_2_7)))]
pub fn mark_movable<T>(value: T)
where
T: Deref<Target = Value>,
{
unsafe { rb_gc_mark_movable(value.as_rb_value()) };
}
/// Get the new location of an object.
///
/// The [`Value`] type is effectly a pointer to a Ruby object. Ruby's garbage
/// collector will avoid moving objects exposed to extensions, unless the
/// object has been marked with [`mark_movable`]. When implementing
/// [`DataTypeFunctions::compact`](`crate::r_typed_data::DataTypeFunctions::compact`)
/// you will need to update any Ruby objects you are storing.
///
/// Returns a new `Value` that is pointing to the object that `value` used to
/// point to. If `value` hasn't moved, simply returns `value`.
#[cfg(any(ruby_gte_2_7, docsrs))]
#[cfg_attr(docsrs, doc(cfg(ruby_gte_2_7)))]
pub fn location<T>(value: T) -> T
where
T: ReprValue,
{
unsafe { T::from_value_unchecked(Value::new(rb_gc_location(value.to_value().as_rb_value()))) }
}
/// Registers `value` to never be garbage collected.
///
/// This is essentially a deliberate memory leak.
pub fn register_mark_object<T>(value: T)
where
T: ReprValue,
{
unsafe { rb_gc_register_mark_object(value.to_value().as_rb_value()) }
}
/// Inform Ruby's garbage collector that `valref` points to a live Ruby object.
///
/// Prevents Ruby moving or collecting `valref`. This should be used on
/// `static` items to prevent them being collected instead of relying on Ruby
/// constants/globals to allways refrence the value.
///
/// See also [`BoxValue`](crate::value::BoxValue).
pub fn register_address<T>(valref: &T)
where
T: ReprValue,
{
unsafe { rb_gc_register_address(valref as *const _ as *mut VALUE) }
}
/// Inform Ruby's garbage collector that `valref` that was previously
/// registered with [`register_address`] no longer points to a live Ruby
/// object.
pub fn unregister_address<T>(valref: &T)
where
T: ReprValue,
{
unsafe { rb_gc_unregister_address(valref as *const _ as *mut VALUE) }
}
/// Disable automatic GC runs.
///
/// This could result in other Ruby api functions unexpectedly raising
/// `NoMemError`.
///
/// Returns `true` if GC was already disabled, `false` otherwise.
pub fn disable() -> bool {
unsafe { Value::new(rb_gc_disable()).to_bool() }
}
/// Enable automatic GC run.
///
/// Garbage Collection is enabled by default, calling this function only makes
/// sense if [`disable`] was previously called.
///
/// Returns `true` if GC was previously disabled, `false` otherwise.
pub fn enable() -> bool {
unsafe { Value::new(rb_gc_enable()).to_bool() }
}
/// Trigger a "full" GC run.
///
/// This will perform a full mark phase and a complete sweep phase, but may not
/// run every single proceess associated with garbage collection.
///
/// Finalisers will be deferred to run later.
///
/// Currently (with versions of Ruby that support compaction) it will not
/// trigger compaction.
pub fn start() {
unsafe { rb_gc_start() };
}
/// Inform Ruby of external memory usage.
///
/// The Ruby GC is run when Ruby thinks it's running out of memory, but won't
/// take into account any memory allocated outside of Ruby api functions. This
/// function can be used to give Ruby a more accurate idea of how much memory
/// the process is using.
///
/// Pass negative numbers to indicate memory has been freed.
pub fn adjust_memory_usage(diff: DiffSize) {
unsafe { rb_gc_adjust_memory_usage(diff) };
}
/// Returns the number of garbage collections that have been run since the
/// start of the process.
pub fn count() -> usize {
unsafe { rb_gc_count() as usize }
}
/// Returns the GC profiling value for `key`.
pub fn stat<T>(key: T) -> Result<usize, Error>
where
T: Into<Symbol>,
{
let sym = key.into();
let mut res = 0;
protect(|| {
res = unsafe { rb_gc_stat(sym.as_rb_value()) as usize };
QNIL
})?;
Ok(res)
}
/// Returns all possible key/value pairs for [`stat`] as a Ruby Hash.
pub fn all_stats() -> RHash {
let res = RHash::new();
unsafe { rb_gc_stat(res.as_rb_value()) };
res
}