1use atomic::Atomic;
2
3use std::sync::atomic::Ordering;
4use std::sync::Arc;
5
6use crate::plan::tracing::{ObjectQueue, OptionObjectQueue};
7use crate::policy::sft::GCWorkerMutRef;
8use crate::policy::sft::SFT;
9use crate::policy::space::{CommonSpace, Space};
10use crate::scheduler::GCWorker;
11use crate::util::address::Address;
12use crate::util::alloc::allocator::AllocationOptions;
13use crate::util::conversions;
14use crate::util::copy::CopySemantics;
15use crate::util::heap::gc_trigger::GCTrigger;
16use crate::util::heap::layout::vm_layout::vm_layout;
17use crate::util::heap::PageResource;
18use crate::util::heap::VMRequest;
19use crate::util::metadata::side_metadata::SideMetadataContext;
20use crate::util::metadata::side_metadata::SideMetadataSanity;
21use crate::util::object_enum::ObjectEnumerator;
22use crate::util::opaque_pointer::*;
23use crate::util::os::*;
24use crate::util::ObjectReference;
25use crate::vm::VMBinding;
26
27pub struct LockFreeImmortalSpace<VM: VMBinding> {
34 #[allow(unused)]
35 name: &'static str,
36 cursor: Atomic<Address>,
38 limit: Address,
40 start: Address,
42 total_bytes: usize,
44 slow_path_zeroing: bool,
46 metadata: SideMetadataContext,
47 gc_trigger: Arc<GCTrigger<VM>>,
48}
49
50impl<VM: VMBinding> SFT for LockFreeImmortalSpace<VM> {
51 fn name(&self) -> &'static str {
52 self.get_name()
53 }
54 fn is_live(&self, _object: ObjectReference) -> bool {
55 unimplemented!()
56 }
57 #[cfg(feature = "object_pinning")]
58 fn pin_object(&self, _object: ObjectReference) -> bool {
59 false
60 }
61 #[cfg(feature = "object_pinning")]
62 fn unpin_object(&self, _object: ObjectReference) -> bool {
63 false
64 }
65 #[cfg(feature = "object_pinning")]
66 fn is_object_pinned(&self, _object: ObjectReference) -> bool {
67 true
68 }
69 fn is_movable(&self) -> bool {
70 unimplemented!()
71 }
72 #[cfg(feature = "sanity")]
73 fn is_sane(&self) -> bool {
74 unimplemented!()
75 }
76 fn initialize_object_metadata(&self, _object: ObjectReference, _bytes: usize) {
77 #[cfg(feature = "vo_bit")]
78 crate::util::metadata::vo_bit::set_vo_bit(_object);
79 }
80 #[cfg(feature = "vo_bit")]
81 fn is_mmtk_object(&self, addr: Address) -> Option<ObjectReference> {
82 crate::util::metadata::vo_bit::is_vo_bit_set_for_addr(addr)
83 }
84 #[cfg(feature = "vo_bit")]
85 fn find_object_from_internal_pointer(
86 &self,
87 ptr: Address,
88 max_search_bytes: usize,
89 ) -> Option<ObjectReference> {
90 crate::util::metadata::vo_bit::find_object_from_internal_pointer::<VM>(
91 ptr,
92 max_search_bytes,
93 )
94 }
95 fn sft_trace_object(
96 &self,
97 _queue: &mut OptionObjectQueue,
98 _object: ObjectReference,
99 _worker: GCWorkerMutRef,
100 ) -> ObjectReference {
101 unreachable!()
102 }
103}
104
105impl<VM: VMBinding> Space<VM> for LockFreeImmortalSpace<VM> {
106 fn as_space(&self) -> &dyn Space<VM> {
107 self
108 }
109 fn as_sft(&self) -> &(dyn SFT + Sync + 'static) {
110 self
111 }
112 fn get_page_resource(&self) -> &dyn PageResource<VM> {
113 unimplemented!()
114 }
115 fn maybe_get_page_resource_mut(&mut self) -> Option<&mut dyn PageResource<VM>> {
116 None
117 }
118 fn common(&self) -> &CommonSpace<VM> {
119 unimplemented!()
120 }
121
122 fn get_gc_trigger(&self) -> &GCTrigger<VM> {
123 &self.gc_trigger
124 }
125
126 fn release_multiple_pages(&mut self, _start: Address) {
127 panic!("immortalspace only releases pages enmasse")
128 }
129
130 fn initialize_sft(&self, sft_map: &mut dyn crate::policy::sft_map::SFTMap) {
131 unsafe { sft_map.eager_initialize(self.as_sft(), self.start, self.total_bytes) };
132 }
133
134 fn initialize_side_metadata(&self) {
135 self.metadata
136 .try_map_metadata_space(self.start, self.total_bytes, self.get_name())
137 .unwrap_or_else(|e| {
138 panic!("failed to mmap meta memory: {e}")
140 });
141 }
142
143 fn estimate_side_meta_pages(&self, data_pages: usize) -> usize {
144 self.metadata.calculate_reserved_pages(data_pages)
145 }
146
147 fn reserved_pages(&self) -> usize {
148 let cursor = self.cursor.load(Ordering::Relaxed);
149 let data_pages = conversions::bytes_to_pages_up(self.limit - cursor);
150 let meta_pages = self.estimate_side_meta_pages(data_pages);
151 data_pages + meta_pages
152 }
153
154 fn acquire(&self, _tls: VMThread, pages: usize, alloc_options: AllocationOptions) -> Address {
155 trace!("LockFreeImmortalSpace::acquire");
156 let bytes = conversions::pages_to_bytes(pages);
157 let start = self
158 .cursor
159 .fetch_update(Ordering::Relaxed, Ordering::Relaxed, |addr| {
160 Some(addr.add(bytes))
161 })
162 .expect("update cursor failed");
163 if start + bytes > self.limit {
164 if alloc_options.allow_oom_call {
165 panic!("OutOfMemory");
166 } else {
167 return Address::ZERO;
168 }
169 }
170 if self.slow_path_zeroing {
171 crate::util::memory::zero(start, bytes);
172 }
173 start
174 }
175
176 fn get_name(&self) -> &'static str {
181 "LockFreeImmortalSpace"
182 }
183
184 fn verify_side_metadata_sanity(&self, side_metadata_sanity_checker: &mut SideMetadataSanity) {
187 side_metadata_sanity_checker
188 .verify_metadata_context(std::any::type_name::<Self>(), &self.metadata)
189 }
190
191 fn enumerate_objects(&self, enumerator: &mut dyn ObjectEnumerator) {
192 enumerator.visit_address_range(self.start, self.start + self.total_bytes);
193 }
194
195 fn clear_side_log_bits(&self) {
196 unimplemented!()
197 }
198
199 fn set_side_log_bits(&self) {
200 unimplemented!()
201 }
202}
203
204impl<VM: VMBinding> crate::policy::gc_work::PolicyTraceObject<VM> for LockFreeImmortalSpace<VM> {
205 fn trace_object<Q: ObjectQueue, const KIND: crate::policy::gc_work::TraceKind>(
206 &self,
207 _queue: &mut Q,
208 _object: ObjectReference,
209 _copy: Option<CopySemantics>,
210 _worker: &mut GCWorker<VM>,
211 ) -> ObjectReference {
212 unreachable!()
213 }
214 fn may_move_objects<const KIND: crate::policy::gc_work::TraceKind>() -> bool {
215 unreachable!()
216 }
217}
218
219impl<VM: VMBinding> LockFreeImmortalSpace<VM> {
220 #[allow(dead_code)] pub fn new(args: crate::policy::space::PlanCreateSpaceArgs<VM>) -> Self {
222 let slow_path_zeroing = args.zeroed;
223
224 let total_bytes = match *args.options.gc_trigger {
226 crate::util::options::GCTriggerSelector::FixedHeapSize(bytes) => bytes,
227 _ => unimplemented!(),
228 };
229 assert!(
230 total_bytes <= vm_layout().available_bytes(),
231 "Initial requested memory ({} bytes) overflows the heap. Max heap size is {} bytes.",
232 total_bytes,
233 vm_layout().available_bytes()
234 );
235 let aligned_total_bytes = crate::util::conversions::raw_align_up(
237 total_bytes,
238 crate::util::heap::vm_layout::BYTES_IN_CHUNK,
239 );
240
241 let vmrequest = VMRequest::fixed_size(aligned_total_bytes);
243 let (extent, align, top) = match vmrequest {
245 VMRequest::Extent { extent, top } => (extent, None, top),
246 VMRequest::AlignedExtent { extent, align, top } => (extent, Some(align), top),
247 _ => unreachable!(),
248 };
249 let anno = MmapAnnotation::Space { name: args.name };
250 let huge_page_option = args.options.transparent_hugepages_as_huge_page_support();
251 let reasonable_extent = CommonSpace::estimate_reasonable_contiguous_extent(
252 &args.options,
253 &args.gc_trigger,
254 args.vm_map,
255 extent,
256 );
257 let start = args
258 .heap
259 .reserve_quarantined(
260 reasonable_extent,
261 align,
262 top,
263 args.mmapper,
264 huge_page_option,
265 &anno,
266 )
267 .unwrap_or_else(|mmap_error| {
268 panic!(
269 "Failed to quarantine contiguous space {} for {} bytes: {}",
270 args.name, reasonable_extent, mmap_error
271 )
272 });
273
274 let space = Self {
275 name: args.name,
276 cursor: Atomic::new(start),
277 limit: start + aligned_total_bytes,
278 start,
279 total_bytes: aligned_total_bytes,
280 slow_path_zeroing,
281 metadata: SideMetadataContext {
282 global: args.global_side_metadata_specs,
283 local: vec![],
284 },
285 gc_trigger: args.gc_trigger,
286 };
287
288 let strategy = MmapStrategy::default()
290 .transparent_hugepages(*args.options.transparent_hugepages)
291 .prot(crate::util::os::MmapProtection::ReadWrite)
292 .replace(true)
293 .reserve(true);
294 crate::util::os::OS::dzmmap(start, aligned_total_bytes, strategy, &anno).unwrap();
295
296 space
297 }
298}