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 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310
// Copyright 2019-2021 the Deno authors. All rights reserved. MIT license
use crate::platform::Platform;
use crate::support::int;
use crate::support::Opaque;
use crate::support::SharedRef;
use crate::support::UniqueRef;
extern "C" {
fn cppgc__initialize_process(platform: *mut Platform);
fn cppgc__shutdown_process();
fn cppgc__heap__create(
platform: *mut Platform,
wrappable_type_index: int,
wrappable_instance_index: int,
embedder_id_for_garbage_collected: u16,
) -> *mut Heap;
fn cppgc__heap__DELETE(heap: *mut Heap);
fn cppgc__make_garbage_collectable(
heap: *mut Heap,
obj: *mut (),
trace: TraceFn,
destroy: DestroyFn,
) -> *mut InnerMember;
fn cppgc__heap__enable_detached_garbage_collections_for_testing(
heap: *mut Heap,
);
fn cppgc__heap__collect_garbage_for_testing(
heap: *mut Heap,
stack_state: EmbedderStackState,
);
fn cppgc__visitor__trace(visitor: *const Visitor, member: *const InnerMember);
}
/// Process-global initialization of the garbage collector. Must be called before
/// creating a Heap.
///
/// Can be called multiple times when paired with `ShutdownProcess()`.
pub fn initalize_process(platform: SharedRef<Platform>) {
unsafe {
cppgc__initialize_process(&*platform as *const Platform as *mut _);
}
}
/// # Safety
///
/// Must be called after destroying the last used heap. Some process-global
/// metadata may not be returned and reused upon a subsequent
/// `initalize_process()` call.
pub unsafe fn shutdown_process() {
cppgc__shutdown_process();
}
/// Visitor passed to trace methods. All managed pointers must have called the
/// Visitor's trace method on them.
///
/// ```
/// use v8::cppgc::{Member, Visitor, GarbageCollected};
///
/// struct Foo { foo: Member<Foo> }
///
/// impl GarbageCollected for Foo {
/// fn trace(&self, visitor: &Visitor) {
/// visitor.trace(&self.foo);
/// }
/// }
/// ```
#[repr(C)]
#[derive(Debug)]
pub struct Visitor(Opaque);
impl Visitor {
pub fn trace<T: GarbageCollected>(&self, member: &Member<T>) {
unsafe { cppgc__visitor__trace(self, member.handle) }
}
}
#[repr(C)]
pub enum EmbedderStackState {
/// Stack may contain interesting heap pointers.
MayContainHeapPointers,
/// Stack does not contain any interesting heap pointers.
NoHeapPointers,
}
/// Specifies supported marking types.
#[repr(u8)]
pub enum MarkingType {
/// Atomic stop-the-world marking. This option does not require any write barriers but is the most intrusive in terms of jank.
Atomic,
/// Incremental marking interleaves marking with the rest of the application workload on the same thread.
Incremental,
/// Incremental and concurrent marking.
IncrementalAndConcurrent,
}
/// Specifies supported sweeping types.
#[repr(u8)]
pub enum SweepingType {
/// Atomic stop-the-world sweeping. All of sweeping is performed at once.
Atomic,
/// Incremental sweeping interleaves sweeping with the rest of the application workload on the same thread.
Incremental,
/// Incremental and concurrent sweeping. Sweeping is split and interleaved with the rest of the application.
IncrementalAndConcurrent,
}
pub type InternalFieldIndex = int;
/// Describes how V8 wrapper objects maintain references to garbage-collected C++ objects.
pub struct WrapperDescriptor {
/// Index of the wrappable type.
pub wrappable_type_index: InternalFieldIndex,
/// Index of the wrappable instance.
pub wrappable_instance_index: InternalFieldIndex,
/// Embedder id identifying instances of garbage-collected objects. It is expected that
/// the first field of the wrappable type is a uint16_t holding the id. Only references
/// to instances of wrappables types with an id of embedder_id_for_garbage_collected will
/// be considered by Heap.
pub embedder_id_for_garbage_collected: u16,
}
impl WrapperDescriptor {
pub fn new(
wrappable_type_index: InternalFieldIndex,
wrappable_instance_index: InternalFieldIndex,
embedder_id_for_garbage_collected: u16,
) -> Self {
Self {
wrappable_type_index,
wrappable_instance_index,
embedder_id_for_garbage_collected,
}
}
}
pub struct HeapCreateParams {
wrapper_descriptor: WrapperDescriptor,
/// Specifies which kind of marking are supported by the heap.
pub marking_support: MarkingType,
/// Specifies which kind of sweeping are supported by the heap.
pub sweeping_support: SweepingType,
}
impl HeapCreateParams {
pub fn new(wrapper_descriptor: WrapperDescriptor) -> Self {
Self {
wrapper_descriptor,
marking_support: MarkingType::IncrementalAndConcurrent,
sweeping_support: SweepingType::IncrementalAndConcurrent,
}
}
}
type TraceFn = unsafe extern "C" fn(*mut (), *mut Visitor);
type DestroyFn = unsafe extern "C" fn(*mut ());
/// A heap for allocating managed C++ objects.
///
/// Similar to v8::Isolate, the heap may only be accessed from one thread at a
/// time.
#[repr(C)]
#[derive(Debug)]
pub struct Heap(Opaque);
impl Drop for Heap {
fn drop(&mut self) {
unsafe { cppgc__heap__DELETE(self as *mut Heap) }
}
}
impl Heap {
pub fn create(
platform: SharedRef<Platform>,
params: HeapCreateParams,
) -> UniqueRef<Heap> {
let WrapperDescriptor {
wrappable_type_index,
wrappable_instance_index,
embedder_id_for_garbage_collected,
} = params.wrapper_descriptor;
unsafe {
UniqueRef::from_raw(cppgc__heap__create(
&*platform as *const Platform as *mut _,
wrappable_type_index,
wrappable_instance_index,
embedder_id_for_garbage_collected,
))
}
}
pub fn collect_garbage_for_testing(&self, stack_state: EmbedderStackState) {
unsafe {
cppgc__heap__collect_garbage_for_testing(
self as *const Heap as *mut _,
stack_state,
);
}
}
pub fn enable_detached_garbage_collections_for_testing(&self) {
unsafe {
cppgc__heap__enable_detached_garbage_collections_for_testing(
self as *const Heap as *mut _,
);
}
}
}
/// Base trait for managed objects.
pub trait GarbageCollected {
fn trace(&self, _visitor: &Visitor) {}
}
#[repr(C)]
pub struct InnerMember {
inner: [usize; 2],
ptr: *mut (),
}
impl InnerMember {
pub unsafe fn get<T: GarbageCollected>(&self) -> &T {
unsafe { self.ptr.cast::<T>().as_ref().unwrap() }
}
}
/// Members are used to contain strong pointers to other garbage
/// collected objects. All members fields on garbage collected objects
/// must be trace in the `trace` method.
#[repr(transparent)]
pub struct Member<T: GarbageCollected> {
pub handle: *mut InnerMember,
_phantom: std::marker::PhantomData<T>,
}
impl<T: GarbageCollected> Member<T> {
/// Returns a raw pointer to the object.
///
/// # Safety
///
/// There are no guarantees that the object is alive and not garbage collected.
pub unsafe fn get(&self) -> &T {
unsafe { (*self.handle).get() }
}
}
impl<T: GarbageCollected> std::ops::Deref for Member<T> {
type Target = T;
fn deref(&self) -> &Self::Target {
unsafe { self.get() }
}
}
/// Constructs an instance of T, which is a garbage collected type.
///
/// The object will be allocated on the heap and managed by cppgc. During
/// marking, the object will be traced by calling the `trace` method on it.
///
/// During sweeping, the destructor will be called and the memory will be
/// freed using `Box::from_raw`.
pub fn make_garbage_collected<T: GarbageCollected>(
heap: &Heap,
obj: Box<T>,
) -> Member<T> {
unsafe extern "C" fn destroy<T>(obj: *mut ()) {
let _ = Box::from_raw(obj as *mut T);
}
unsafe { make_garbage_collected_raw(heap, Box::into_raw(obj), destroy::<T>) }
}
/// # Safety
///
/// By calling this function, you are giving up ownership of `T` to the
/// garbage collector.
///
/// `obj` must be a pointer to a valid instance of T allocated on the heap.
///
/// `drop_fn` must be a function that drops the instance of T. This function
/// will be called when the object is garbage collected.
pub unsafe fn make_garbage_collected_raw<T: GarbageCollected>(
heap: &Heap,
obj: *mut T,
destroy: DestroyFn,
) -> Member<T> {
unsafe extern "C" fn trace<T: GarbageCollected>(
obj: *mut (),
visitor: *mut Visitor,
) {
let obj = unsafe { &*(obj as *const T) };
obj.trace(unsafe { &*visitor });
}
let handle = cppgc__make_garbage_collectable(
heap as *const Heap as *mut _,
obj as _,
trace::<T>,
destroy,
);
Member {
handle,
_phantom: std::marker::PhantomData,
}
}