Skip to content
Merged
Show file tree
Hide file tree
Changes from 4 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions docs/userguide/src/tutorial/code/mygc_semispace/global.rs
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,13 @@ impl<VM: VMBinding> Plan for MyGC<VM> {
}
// ANCHOR_END: create_copy_config

// Modify
// ANCHOR: current_gc_may_move_object
fn current_gc_may_move_object(&self) -> bool {
true
}
// ANCHOR_END: current_gc_may_move_object

// Modify
// ANCHOR: schedule_collection
fn schedule_collection(&'static self, scheduler: &GCWorkScheduler<VM>) {
Expand Down
7 changes: 7 additions & 0 deletions docs/userguide/src/tutorial/mygc/ss/collection.md
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,13 @@ space here.
{{#include ../../code/mygc_semispace/global.rs:create_copy_config}}
```

Because the semispace GC copies objects in every single GC, we modify the method
`current_gc_may_move_object()` in `MyGC` so that it always returns `true`.

```rust
{{#include ../../code/mygc_semispace/global.rs:current_gc_may_move_object}}
```

## Introduce collection to MyGC plan

Add a new method to `Plan for MyGC`, `schedule_collection()`. This function
Expand Down
4 changes: 4 additions & 0 deletions src/plan/generational/copying/global.rs
Original file line number Diff line number Diff line change
Expand Up @@ -127,6 +127,10 @@ impl<VM: VMBinding> Plan for GenCopy<VM> {
self.gen.get_used_pages() + self.tospace().reserved_pages()
}

fn current_gc_may_move_object(&self) -> bool {
true
}

/// Return the number of pages available for allocation. Assuming all future allocations goes to nursery.
fn get_available_pages(&self) -> usize {
// super.get_available_pages() / 2 to reserve pages for copying
Expand Down
16 changes: 12 additions & 4 deletions src/plan/generational/immix/global.rs
Original file line number Diff line number Diff line change
Expand Up @@ -137,10 +137,7 @@ impl<VM: VMBinding> Plan for GenImmix<VM> {
let full_heap = !self.gen.is_current_gc_nursery();
self.gen.release(tls);
if full_heap {
let did_defrag = self.immix_space.release(full_heap);
self.last_gc_was_defrag.store(did_defrag, Ordering::Relaxed);
} else {
self.last_gc_was_defrag.store(false, Ordering::Relaxed);
self.immix_space.release(full_heap);
}
self.last_gc_was_full_heap
.store(full_heap, Ordering::Relaxed);
Expand All @@ -149,6 +146,17 @@ impl<VM: VMBinding> Plan for GenImmix<VM> {
fn end_of_gc(&mut self, _tls: VMWorkerThread) {
self.gen
.set_next_gc_full_heap(CommonGenPlan::should_next_gc_be_full_heap(self));

let did_defrag = self.immix_space.end_of_gc();
self.last_gc_was_defrag.store(did_defrag, Ordering::Relaxed);
}

fn current_gc_may_move_object(&self) -> bool {
if self.is_current_gc_nursery() {
true
} else {
self.immix_space.in_defrag()
}
}

fn get_collection_reserved_pages(&self) -> usize {
Expand Down
10 changes: 10 additions & 0 deletions src/plan/global.rs
Original file line number Diff line number Diff line change
Expand Up @@ -299,6 +299,16 @@ pub trait Plan: 'static + HasSpaces + Sync + Downcast {
true
}

/// Return whether the current GC may move any object. The VM binding can make use of this
/// information and choose to or not to update some data structures that record the addresses
/// of objects.
///
/// This function is callable during a GC. From the VM binding's point of view, the information
/// of whether the current GC is a defrag GC is available since `Collection::stop_mutators` is
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Mentioning defrag could be confusing here.

Suggested change
/// of whether the current GC is a defrag GC is available since `Collection::stop_mutators` is
/// of whether the current GC moves object or not is available since `Collection::stop_mutators` is

/// called, and remains available until (but not including) `resume_mutators` at which time the
/// current GC has just finished.
fn current_gc_may_move_object(&self) -> bool;

/// An object is firstly reached by a sanity GC. So the object is reachable
/// in the current GC, and all the GC work has been done for the object (such as
/// tracing and releasing). A plan can implement this to
Expand Down
10 changes: 9 additions & 1 deletion src/plan/immix/global.rs
Original file line number Diff line number Diff line change
Expand Up @@ -93,8 +93,16 @@ impl<VM: VMBinding> Plan for Immix<VM> {
fn release(&mut self, tls: VMWorkerThread) {
self.common.release(tls, true);
// release the collected region
self.immix_space.release(true);
}

fn end_of_gc(&mut self, _tls: VMWorkerThread) {
self.last_gc_was_defrag
.store(self.immix_space.release(true), Ordering::Relaxed);
.store(self.immix_space.end_of_gc(), Ordering::Relaxed);
}

fn current_gc_may_move_object(&self) -> bool {
self.immix_space.in_defrag()
}

fn get_collection_reserved_pages(&self) -> usize {
Expand Down
4 changes: 4 additions & 0 deletions src/plan/markcompact/global.rs
Original file line number Diff line number Diff line change
Expand Up @@ -171,6 +171,10 @@ impl<VM: VMBinding> Plan for MarkCompact<VM> {
fn get_collection_reserved_pages(&self) -> usize {
0
}

fn current_gc_may_move_object(&self) -> bool {
true
}
}

impl<VM: VMBinding> MarkCompact<VM> {
Expand Down
4 changes: 4 additions & 0 deletions src/plan/marksweep/global.rs
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,10 @@ impl<VM: VMBinding> Plan for MarkSweep<VM> {
self.base().collection_required(self, space_full)
}

fn current_gc_may_move_object(&self) -> bool {
false
}

fn get_used_pages(&self) -> usize {
self.common.get_used_pages() + self.ms.reserved_pages()
}
Expand Down
4 changes: 4 additions & 0 deletions src/plan/nogc/global.rs
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,10 @@ impl<VM: VMBinding> Plan for NoGC<VM> {
unreachable!("GC triggered in nogc")
}

fn current_gc_may_move_object(&self) -> bool {
false
}

fn get_used_pages(&self) -> usize {
self.nogc_space.reserved_pages()
+ self.immortal.reserved_pages()
Expand Down
4 changes: 4 additions & 0 deletions src/plan/pageprotect/global.rs
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,10 @@ impl<VM: VMBinding> Plan for PageProtect<VM> {
self.base().collection_required(self, space_full)
}

fn current_gc_may_move_object(&self) -> bool {
false
}

fn get_used_pages(&self) -> usize {
self.space.reserved_pages() + self.common.get_used_pages()
}
Expand Down
4 changes: 4 additions & 0 deletions src/plan/semispace/global.rs
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,10 @@ impl<VM: VMBinding> Plan for SemiSpace<VM> {
self.base().collection_required(self, space_full)
}

fn current_gc_may_move_object(&self) -> bool {
true
}

fn get_collection_reserved_pages(&self) -> usize {
self.tospace().reserved_pages()
}
Expand Down
15 changes: 12 additions & 3 deletions src/plan/sticky/immix/global.rs
Original file line number Diff line number Diff line change
Expand Up @@ -126,9 +126,6 @@ impl<VM: VMBinding> Plan for StickyImmix<VM> {

fn release(&mut self, tls: crate::util::VMWorkerThread) {
if self.is_current_gc_nursery() {
let was_defrag = self.immix.immix_space.release(false);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We still need to call immix_space.release() here, right?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes. New objects can be allocated into the immix_space and the LOS, and we still need to release them. I'll fix it.

self.immix
.set_last_gc_was_defrag(was_defrag, Ordering::Relaxed);
self.immix.common.los.release(false);
} else {
self.immix.release(tls);
Expand All @@ -140,6 +137,10 @@ impl<VM: VMBinding> Plan for StickyImmix<VM> {
crate::plan::generational::global::CommonGenPlan::should_next_gc_be_full_heap(self);
self.next_gc_full_heap
.store(next_gc_full_heap, Ordering::Relaxed);

let was_defrag = self.immix.immix_space.end_of_gc();
self.immix
.set_last_gc_was_defrag(was_defrag, Ordering::Relaxed);
}

fn collection_required(&self, space_full: bool, space: Option<SpaceStats<Self::VM>>) -> bool {
Expand All @@ -158,6 +159,14 @@ impl<VM: VMBinding> Plan for StickyImmix<VM> {
self.gc_full_heap.load(Ordering::Relaxed) && self.immix.last_collection_was_exhaustive()
}

fn current_gc_may_move_object(&self) -> bool {
if self.is_current_gc_nursery() {
!cfg!(feature = "sticky_immix_non_moving_nursery")
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This might be easier to understand and less error prone than directly checking the feature.

Suggested change
!cfg!(feature = "sticky_immix_non_moving_nursery")
crate::policy::immix_space::PREFER_COPY_ON_NURSERY_GC

} else {
return self.get_immix_space().in_defrag();
}
}

fn get_collection_reserved_pages(&self) -> usize {
self.immix.get_collection_reserved_pages()
}
Expand Down
4 changes: 2 additions & 2 deletions src/policy/immix/defrag.rs
Original file line number Diff line number Diff line change
Expand Up @@ -205,9 +205,9 @@ impl Defrag {
.store(threshold, Ordering::Release);
}

/// Release work. Should be called in ImmixSpace::release.
/// Reset the in-defrag state.
#[allow(clippy::assertions_on_constants)]
pub fn release<VM: VMBinding>(&self, _space: &ImmixSpace<VM>) {
pub fn reset_in_defrag(&self) {
debug_assert!(super::DEFRAG);
self.in_defrag_collection.store(false, Ordering::Release);
}
Expand Down
19 changes: 11 additions & 8 deletions src/policy/immix/immixspace.rs
Original file line number Diff line number Diff line change
Expand Up @@ -440,12 +440,10 @@ impl<VM: VMBinding> ImmixSpace<VM> {
}
}

/// Release for the immix space. This is called when a GC finished.
/// Return whether this GC was a defrag GC, as a plan may want to know this.
pub fn release(&mut self, major_gc: bool) -> bool {
let did_defrag = self.defrag.in_defrag();
/// Release for the immix space.
pub fn release(&mut self, major_gc: bool) {
if major_gc {
// Update line_unavail_state for hole searching afte this GC.
// Update line_unavail_state for hole searching after this GC.
if !super::BLOCK_ONLY {
self.line_unavail_state.store(
self.line_mark_state.load(Ordering::Acquire),
Expand All @@ -460,12 +458,17 @@ impl<VM: VMBinding> ImmixSpace<VM> {
// Sweep chunks and blocks
let work_packets = self.generate_sweep_tasks();
self.scheduler().work_buckets[WorkBucketStage::Release].bulk_add(work_packets);
if super::DEFRAG {
self.defrag.release(self);
}

self.lines_consumed.store(0, Ordering::Relaxed);
}

/// This is called when a GC finished.
/// Return whether this GC was a defrag GC, as a plan may want to know this.
pub fn end_of_gc(&mut self) -> bool {
let did_defrag = self.defrag.in_defrag();
if super::DEFRAG {
self.defrag.reset_in_defrag();
}
did_defrag
}

Expand Down