@@ -12,6 +12,7 @@ use crate::scheduler::CoordinatorMessage;
1212use crate :: util:: VMWorkerThread ;
1313use crate :: vm:: VMBinding ;
1414use crate :: MMTK ;
15+ use atomic:: Ordering ;
1516
1617use super :: { GCWork , GCWorkScheduler , GCWorker } ;
1718
@@ -66,33 +67,54 @@ impl<VM: VMBinding> GCController<VM> {
6667 }
6768 }
6869
69- /// Coordinate workers to perform GC in response to a GC request .
70- pub fn do_gc_until_completion ( & mut self ) {
70+ /// Process a message. Return true if the GC is finished .
71+ fn process_message ( & mut self , message : CoordinatorMessage < VM > ) -> bool {
7172 let worker = & mut self . coordinator_worker ;
7273 let mmtk = self . mmtk ;
74+ match message {
75+ CoordinatorMessage :: Work ( mut work) => {
76+ work. do_work_with_stat ( worker, mmtk) ;
77+ self . scheduler
78+ . pending_messages
79+ . fetch_sub ( 1 , Ordering :: SeqCst ) ;
80+ false
81+ }
82+ CoordinatorMessage :: Finish => {
83+ self . scheduler
84+ . pending_messages
85+ . fetch_sub ( 1 , Ordering :: SeqCst ) ;
86+ // Quit only if all the buckets are empty.
87+ // For concurrent GCs, the coordinator thread may receive this message when
88+ // some buckets are still not empty. Under such case, the coordinator
89+ // should ignore the message.
90+ let _guard = self . scheduler . worker_monitor . 0 . lock ( ) . unwrap ( ) ;
91+ self . scheduler . worker_group . all_parked ( ) && self . scheduler . all_buckets_empty ( )
92+ }
93+ }
94+ }
7395
96+ /// Coordinate workers to perform GC in response to a GC request.
97+ pub fn do_gc_until_completion ( & mut self ) {
7498 // Schedule collection.
75- ScheduleCollection . do_work_with_stat ( worker , mmtk) ;
99+ ScheduleCollection . do_work_with_stat ( & mut self . coordinator_worker , self . mmtk ) ;
76100
77101 // Drain the message queue and execute coordinator work.
78102 loop {
79103 let message = self . receiver . recv ( ) . unwrap ( ) ;
80- match message {
81- CoordinatorMessage :: Work ( mut work) => {
82- work. do_work_with_stat ( worker, mmtk) ;
83- }
84- CoordinatorMessage :: AllWorkerParked | CoordinatorMessage :: BucketDrained => {
85- self . scheduler . update_buckets ( ) ;
86- }
87- }
88- let _guard = self . scheduler . worker_monitor . 0 . lock ( ) . unwrap ( ) ;
89- if self . scheduler . all_workers_parked ( ) && self . scheduler . all_buckets_empty ( ) {
104+ let finished = self . process_message ( message) ;
105+ if finished {
90106 break ;
91107 }
92108 }
109+ debug_assert ! ( !self . scheduler. worker_group. has_designated_work( ) ) ;
110+ // Sometimes multiple finish messages will be sent. Skip them.
93111 for message in self . receiver . try_iter ( ) {
94- if let CoordinatorMessage :: Work ( mut work) = message {
95- work. do_work_with_stat ( worker, mmtk) ;
112+ self . scheduler
113+ . pending_messages
114+ . fetch_sub ( 1 , Ordering :: SeqCst ) ;
115+ match message {
116+ CoordinatorMessage :: Work ( _) => unreachable ! ( ) ,
117+ CoordinatorMessage :: Finish => { }
96118 }
97119 }
98120 self . scheduler . deactivate_all ( ) ;
@@ -101,7 +123,7 @@ impl<VM: VMBinding> GCController<VM> {
101123 // Otherwise, for generational GCs, workers will receive and process
102124 // newly generated remembered-sets from those open buckets.
103125 // But these remsets should be preserved until next GC.
104- EndOfGC . do_work_with_stat ( worker , mmtk) ;
126+ EndOfGC . do_work_with_stat ( & mut self . coordinator_worker , self . mmtk ) ;
105127
106128 self . scheduler . debug_assert_all_buckets_deactivated ( ) ;
107129 }
0 commit comments