11use atomic:: Ordering ;
22
33use crate :: global_state:: GlobalState ;
4- use crate :: plan:: gc_requester:: GCRequester ;
54use crate :: plan:: Plan ;
65use crate :: policy:: space:: Space ;
6+ use crate :: scheduler:: GCWorkScheduler ;
77use crate :: util:: constants:: BYTES_IN_PAGE ;
88use crate :: util:: conversions;
99use crate :: util:: options:: { GCTriggerSelector , Options , DEFAULT_MAX_NURSERY , DEFAULT_MIN_NURSERY } ;
10+ use crate :: vm:: Collection ;
1011use crate :: vm:: VMBinding ;
1112use crate :: MMTK ;
1213use std:: mem:: MaybeUninit ;
13- use std:: sync:: atomic:: AtomicUsize ;
14+ use std:: sync:: atomic:: { AtomicBool , AtomicUsize } ;
1415use std:: sync:: Arc ;
1516
1617/// GCTrigger is responsible for triggering GCs based on the given policy.
@@ -23,15 +24,18 @@ pub struct GCTrigger<VM: VMBinding> {
2324 plan : MaybeUninit < & ' static dyn Plan < VM = VM > > ,
2425 /// The triggering policy.
2526 pub policy : Box < dyn GCTriggerPolicy < VM > > ,
26- gc_requester : Arc < GCRequester < VM > > ,
27+ /// Set by mutators to trigger GC. It is atomic so that mutators can check if GC has already
28+ /// been requested efficiently in `poll` without acquiring any mutex.
29+ request_flag : AtomicBool ,
30+ scheduler : Arc < GCWorkScheduler < VM > > ,
2731 options : Arc < Options > ,
2832 state : Arc < GlobalState > ,
2933}
3034
3135impl < VM : VMBinding > GCTrigger < VM > {
3236 pub fn new (
3337 options : Arc < Options > ,
34- gc_requester : Arc < GCRequester < VM > > ,
38+ scheduler : Arc < GCWorkScheduler < VM > > ,
3539 state : Arc < GlobalState > ,
3640 ) -> Self {
3741 GCTrigger {
@@ -58,7 +62,8 @@ impl<VM: VMBinding> GCTrigger<VM> {
5862 }
5963 } ,
6064 options,
61- gc_requester,
65+ request_flag : AtomicBool :: new ( false ) ,
66+ scheduler,
6267 state,
6368 }
6469 }
@@ -72,6 +77,28 @@ impl<VM: VMBinding> GCTrigger<VM> {
7277 unsafe { self . plan . assume_init ( ) }
7378 }
7479
80+ /// Request a GC. Called by mutators when polling (during allocation) and when handling user
81+ /// GC requests (e.g. `System.gc();` in Java).
82+ fn request ( & self ) {
83+ if self . request_flag . load ( Ordering :: Relaxed ) {
84+ return ;
85+ }
86+
87+ if !self . request_flag . swap ( true , Ordering :: Relaxed ) {
88+ // `GCWorkScheduler::request_schedule_collection` needs to hold a mutex to communicate
89+ // with GC workers, which is expensive for functions like `poll`. We use the atomic
90+ // flag `request_flag` to elide the need to acquire the mutex in subsequent calls.
91+ probe ! ( mmtk, gc_requested) ;
92+ self . scheduler . request_schedule_collection ( ) ;
93+ }
94+ }
95+
96+ /// Clear the "GC requested" flag so that mutators can trigger the next GC.
97+ /// Called by a GC worker when all mutators have come to a stop.
98+ pub fn clear_request ( & self ) {
99+ self . request_flag . store ( false , Ordering :: Relaxed ) ;
100+ }
101+
75102 /// This method is called periodically by the allocation subsystem
76103 /// (by default, each time a page is consumed), and provides the
77104 /// collector with an opportunity to collect.
@@ -80,7 +107,11 @@ impl<VM: VMBinding> GCTrigger<VM> {
80107 /// * `space_full`: Space request failed, must recover pages within 'space'.
81108 /// * `space`: The space that triggered the poll. This could `None` if the poll is not triggered by a space.
82109 pub fn poll ( & self , space_full : bool , space : Option < & dyn Space < VM > > ) -> bool {
83- let plan = unsafe { self . plan . assume_init ( ) } ;
110+ if !VM :: VMCollection :: is_collection_enabled ( ) {
111+ return false ;
112+ }
113+
114+ let plan = self . plan ( ) ;
84115 if self
85116 . policy
86117 . is_gc_required ( space_full, space. map ( |s| SpaceStats :: new ( s) ) , plan)
@@ -96,12 +127,62 @@ impl<VM: VMBinding> GCTrigger<VM> {
96127 plan. get_reserved_pages( ) ,
97128 plan. get_total_pages( ) ,
98129 ) ;
99- self . gc_requester . request ( ) ;
130+ self . request ( ) ;
100131 return true ;
101132 }
102133 false
103134 }
104135
136+ /// This method is called when the user manually requests a collection, such as `System.gc()` in Java.
137+ /// Returns true if a collection is actually requested.
138+ ///
139+ /// # Arguments
140+ /// * `force`: If true, we force a collection regardless of the settings. If false, we only trigger a collection if the settings allow it.
141+ /// * `exhaustive`: If true, we try to make the collection exhaustive (e.g. full heap collection). If false, the collection kind is determined internally.
142+ pub fn handle_user_collection_request ( & self , force : bool , exhaustive : bool ) -> bool {
143+ if !self . plan ( ) . constraints ( ) . collects_garbage {
144+ warn ! ( "User attempted a collection request, but the plan can not do GC. The request is ignored." ) ;
145+ return false ;
146+ }
147+
148+ if force || !* self . options . ignore_system_gc && VM :: VMCollection :: is_collection_enabled ( ) {
149+ info ! ( "User triggering collection" ) ;
150+ // TODO: this may not work reliably. If a GC has been triggered, this will not force it to be a full heap GC.
151+ if exhaustive {
152+ if let Some ( gen) = self . plan ( ) . generational ( ) {
153+ gen. force_full_heap_collection ( ) ;
154+ }
155+ }
156+
157+ self . state
158+ . user_triggered_collection
159+ . store ( true , Ordering :: Relaxed ) ;
160+ self . request ( ) ;
161+ return true ;
162+ }
163+
164+ false
165+ }
166+
167+ /// MMTK has requested stop-the-world activity (e.g., stw within a concurrent gc).
168+ // TODO: We should use this for concurrent GC. E.g. in concurrent Immix, when the initial mark is done, we
169+ // can use this function to immediately trigger the final mark pause. The current implementation uses
170+ // normal collection_required check, which may delay the final mark unnecessarily.
171+ #[ allow( unused) ]
172+ pub fn trigger_internal_collection_request ( & self ) {
173+ self . state
174+ . last_internal_triggered_collection
175+ . store ( true , Ordering :: Relaxed ) ;
176+ self . state
177+ . internal_triggered_collection
178+ . store ( true , Ordering :: Relaxed ) ;
179+ // TODO: The current `request()` is probably incorrect for internally triggered GC.
180+ // Consider removing functions related to "internal triggered collection".
181+ self . request ( ) ;
182+ // TODO: Make sure this function works correctly for concurrent GC.
183+ unimplemented ! ( )
184+ }
185+
105186 pub fn should_do_stress_gc ( & self ) -> bool {
106187 Self :: should_do_stress_gc_inner ( & self . state , & self . options )
107188 }
0 commit comments