1
1
use super :: stat:: SchedulerStat ;
2
+ use super :: work_bucket:: WorkBucketStage :: * ;
2
3
use super :: work_bucket:: * ;
3
- use super :: worker:: { GCWorker , GCWorkerShared , WorkerGroup } ;
4
+ use super :: worker:: { GCWorker , GCWorkerShared } ;
4
5
use super :: * ;
5
6
use crate :: mmtk:: MMTK ;
6
7
use crate :: util:: opaque_pointer:: * ;
@@ -10,23 +11,24 @@ use enum_map::{enum_map, EnumMap};
10
11
use std:: collections:: HashMap ;
11
12
use std:: sync:: atomic:: Ordering ;
12
13
use std:: sync:: mpsc:: channel;
13
- use std:: sync:: { Arc , Condvar , Mutex , RwLock } ;
14
+ use std:: sync:: { Arc , Condvar , Mutex } ;
14
15
15
16
pub enum CoordinatorMessage < VM : VMBinding > {
16
17
Work ( Box < dyn CoordinatorWork < VM > > ) ,
17
18
AllWorkerParked ,
18
19
BucketDrained ,
19
20
}
20
21
22
+ /// The shared data structure for distributing work packets between worker threads and the coordinator thread.
21
23
pub struct GCWorkScheduler < VM : VMBinding > {
22
- /// Work buckets
24
+ /// Work buckets for worker threads
23
25
pub work_buckets : EnumMap < WorkBucketStage , WorkBucket < VM > > ,
24
26
/// Work for the coordinator thread
25
27
pub coordinator_work : WorkBucket < VM > ,
26
28
/// The shared parts of GC workers
27
- worker_group : Option < WorkerGroup < VM > > ,
29
+ pub workers_shared : Vec < Arc < GCWorkerShared < VM > > > ,
28
30
/// The shared part of the GC worker object of the controller thread
29
- coordinator_worker_shared : Option < RwLock < Arc < GCWorkerShared < VM > > > > ,
31
+ coordinator_worker_shared : Arc < GCWorkerShared < VM > > ,
30
32
/// Condition Variable for worker synchronization
31
33
pub worker_monitor : Arc < ( Mutex < ( ) > , Condvar ) > ,
32
34
/// A callback to be fired after the `Closure` bucket is drained.
@@ -41,79 +43,42 @@ pub struct GCWorkScheduler<VM: VMBinding> {
41
43
closure_end : Mutex < Option < Box < dyn Send + Fn ( ) -> bool > > > ,
42
44
}
43
45
44
- // The 'channel' inside Scheduler disallows Sync for Scheduler. We have to make sure we use channel properly:
45
- // 1. We should never directly use Sender. We clone the sender and let each worker have their own copy.
46
- // 2. Only the coordinator can use Receiver.
47
- // TODO: We should remove channel from Scheduler, and directly send Sender/Receiver when creating the coordinator and
48
- // the workers.
46
+ // FIXME: GCWorkScheduler should be naturally Sync, but we cannot remove this `impl` yet.
47
+ // Some subtle interaction between ObjectRememberingBarrier, Mutator and some GCWork instances
48
+ // makes the compiler think WorkBucket is not Sync.
49
49
unsafe impl < VM : VMBinding > Sync for GCWorkScheduler < VM > { }
50
50
51
51
impl < VM : VMBinding > GCWorkScheduler < VM > {
52
- pub fn new ( ) -> Arc < Self > {
52
+ pub fn new ( num_workers : usize ) -> Arc < Self > {
53
53
let worker_monitor: Arc < ( Mutex < ( ) > , Condvar ) > = Default :: default ( ) ;
54
- Arc :: new ( Self {
55
- work_buckets : enum_map ! {
56
- WorkBucketStage :: Unconstrained => WorkBucket :: new( true , worker_monitor. clone( ) ) ,
57
- WorkBucketStage :: Prepare => WorkBucket :: new( false , worker_monitor. clone( ) ) ,
58
- WorkBucketStage :: Closure => WorkBucket :: new( false , worker_monitor. clone( ) ) ,
59
- WorkBucketStage :: RefClosure => WorkBucket :: new( false , worker_monitor. clone( ) ) ,
60
- WorkBucketStage :: CalculateForwarding => WorkBucket :: new( false , worker_monitor. clone( ) ) ,
61
- WorkBucketStage :: RefForwarding => WorkBucket :: new( false , worker_monitor. clone( ) ) ,
62
- WorkBucketStage :: Compact => WorkBucket :: new( false , worker_monitor. clone( ) ) ,
63
- WorkBucketStage :: Release => WorkBucket :: new( false , worker_monitor. clone( ) ) ,
64
- WorkBucketStage :: Final => WorkBucket :: new( false , worker_monitor. clone( ) ) ,
65
- } ,
66
- coordinator_work : WorkBucket :: new ( true , worker_monitor. clone ( ) ) ,
67
- worker_group : None ,
68
- coordinator_worker_shared : None ,
69
- worker_monitor,
70
- closure_end : Mutex :: new ( None ) ,
71
- } )
72
- }
73
54
74
- #[ inline]
75
- pub fn num_workers ( & self ) -> usize {
76
- self . worker_group . as_ref ( ) . unwrap ( ) . worker_count ( )
77
- }
78
-
79
- pub fn initialize (
80
- self : & ' static Arc < Self > ,
81
- num_workers : usize ,
82
- mmtk : & ' static MMTK < VM > ,
83
- tls : VMThread ,
84
- ) {
85
- use crate :: scheduler:: work_bucket:: WorkBucketStage :: * ;
86
- let num_workers = if cfg ! ( feature = "single_worker" ) {
87
- 1
88
- } else {
89
- num_workers
55
+ // Create work buckets for workers.
56
+ let mut work_buckets = enum_map ! {
57
+ WorkBucketStage :: Unconstrained => WorkBucket :: new( true , worker_monitor. clone( ) ) ,
58
+ WorkBucketStage :: Prepare => WorkBucket :: new( false , worker_monitor. clone( ) ) ,
59
+ WorkBucketStage :: Closure => WorkBucket :: new( false , worker_monitor. clone( ) ) ,
60
+ WorkBucketStage :: RefClosure => WorkBucket :: new( false , worker_monitor. clone( ) ) ,
61
+ WorkBucketStage :: CalculateForwarding => WorkBucket :: new( false , worker_monitor. clone( ) ) ,
62
+ WorkBucketStage :: RefForwarding => WorkBucket :: new( false , worker_monitor. clone( ) ) ,
63
+ WorkBucketStage :: Compact => WorkBucket :: new( false , worker_monitor. clone( ) ) ,
64
+ WorkBucketStage :: Release => WorkBucket :: new( false , worker_monitor. clone( ) ) ,
65
+ WorkBucketStage :: Final => WorkBucket :: new( false , worker_monitor. clone( ) ) ,
90
66
} ;
91
67
92
- let ( sender, receiver) = channel :: < CoordinatorMessage < VM > > ( ) ;
93
-
94
- let mut self_mut = self . clone ( ) ;
95
- let self_mut = unsafe { Arc :: get_mut_unchecked ( & mut self_mut) } ;
96
-
97
- let coordinator_worker = GCWorker :: new ( mmtk, 0 , self . clone ( ) , true , sender. clone ( ) ) ;
98
- self_mut. coordinator_worker_shared = Some ( RwLock :: new ( coordinator_worker. shared . clone ( ) ) ) ;
99
-
100
- let ( worker_group, spawn_workers) =
101
- WorkerGroup :: new ( mmtk, num_workers, self . clone ( ) , sender) ;
102
- self_mut. worker_group = Some ( worker_group) ;
103
-
68
+ // Set the open condition of each bucket.
104
69
{
105
70
// Unconstrained is always open. Prepare will be opened at the beginning of a GC.
106
71
// This vec will grow for each stage we call with open_next()
107
72
let mut open_stages: Vec < WorkBucketStage > = vec ! [ Unconstrained , Prepare ] ;
108
73
// The rest will open after the previous stage is done.
109
74
let mut open_next = |s : WorkBucketStage | {
110
75
let cur_stages = open_stages. clone ( ) ;
111
- self_mut . work_buckets [ s] . set_open_condition ( move || {
112
- let should_open =
113
- self . are_buckets_drained ( & cur_stages ) && self . worker_group ( ) . all_parked ( ) ;
76
+ work_buckets[ s] . set_open_condition ( move |scheduler : & GCWorkScheduler < VM > | {
77
+ let should_open = scheduler . are_buckets_drained ( & cur_stages )
78
+ && scheduler . all_workers_parked ( ) ;
114
79
// Additional check before the `RefClosure` bucket opens.
115
80
if should_open && s == WorkBucketStage :: RefClosure {
116
- if let Some ( closure_end) = self . closure_end . lock ( ) . unwrap ( ) . as_ref ( ) {
81
+ if let Some ( closure_end) = scheduler . closure_end . lock ( ) . unwrap ( ) . as_ref ( ) {
117
82
if closure_end ( ) {
118
83
// Don't open `RefClosure` if `closure_end` added more works to `Closure`.
119
84
return false ;
@@ -134,18 +99,72 @@ impl<VM: VMBinding> GCWorkScheduler<VM> {
134
99
open_next ( Final ) ;
135
100
}
136
101
137
- // Now that the scheduler is initialized, we spawn the worker threads and the controller thread.
138
- spawn_workers ( tls) ;
102
+ // Create the work bucket for the controller.
103
+ let coordinator_work = WorkBucket :: new ( true , worker_monitor. clone ( ) ) ;
104
+
105
+ // We prepare the shared part of workers, but do not create the actual workers now.
106
+ // The shared parts of workers are communication hubs between controller and workers.
107
+ let workers_shared = ( 0 ..num_workers)
108
+ . map ( |_| Arc :: new ( GCWorkerShared :: < VM > :: new ( worker_monitor. clone ( ) ) ) )
109
+ . collect :: < Vec < _ > > ( ) ;
110
+
111
+ // Similarly, we create the shared part of the work of the controller, but not the controller itself.
112
+ let coordinator_worker_shared = Arc :: new ( GCWorkerShared :: < VM > :: new ( worker_monitor. clone ( ) ) ) ;
113
+
114
+ Arc :: new ( Self {
115
+ work_buckets,
116
+ coordinator_work,
117
+ workers_shared,
118
+ coordinator_worker_shared,
119
+ worker_monitor,
120
+ closure_end : Mutex :: new ( None ) ,
121
+ } )
122
+ }
123
+
124
+ #[ inline]
125
+ pub fn num_workers ( & self ) -> usize {
126
+ self . workers_shared . len ( )
127
+ }
128
+
129
+ pub fn all_workers_parked ( & self ) -> bool {
130
+ self . workers_shared . iter ( ) . all ( |w| w. is_parked ( ) )
131
+ }
132
+
133
+ /// Create GC threads, including the controller thread and all workers.
134
+ pub fn spawn_gc_threads ( self : & Arc < Self > , mmtk : & ' static MMTK < VM > , tls : VMThread ) {
135
+ // Create the communication channel.
136
+ let ( sender, receiver) = channel :: < CoordinatorMessage < VM > > ( ) ;
139
137
138
+ // Spawn the controller thread.
139
+ let coordinator_worker = GCWorker :: new (
140
+ mmtk,
141
+ 0 ,
142
+ self . clone ( ) ,
143
+ true ,
144
+ sender. clone ( ) ,
145
+ self . coordinator_worker_shared . clone ( ) ,
146
+ ) ;
140
147
let gc_controller = GCController :: new (
141
148
mmtk,
142
149
mmtk. plan . base ( ) . gc_requester . clone ( ) ,
143
150
self . clone ( ) ,
144
151
receiver,
145
152
coordinator_worker,
146
153
) ;
147
-
148
154
VM :: VMCollection :: spawn_gc_thread ( tls, GCThreadContext :: < VM > :: Controller ( gc_controller) ) ;
155
+
156
+ // Spawn each worker thread.
157
+ for ( ordinal, shared) in self . workers_shared . iter ( ) . enumerate ( ) {
158
+ let worker = Box :: new ( GCWorker :: new (
159
+ mmtk,
160
+ ordinal,
161
+ self . clone ( ) ,
162
+ false ,
163
+ sender. clone ( ) ,
164
+ shared. clone ( ) ,
165
+ ) ) ;
166
+ VM :: VMCollection :: spawn_gc_thread ( tls, GCThreadContext :: < VM > :: Worker ( worker) ) ;
167
+ }
149
168
}
150
169
151
170
/// Schedule all the common work packets
@@ -206,10 +225,6 @@ impl<VM: VMBinding> GCWorkScheduler<VM> {
206
225
* self . closure_end . lock ( ) . unwrap ( ) = Some ( f) ;
207
226
}
208
227
209
- pub fn worker_group ( & self ) -> & WorkerGroup < VM > {
210
- self . worker_group . as_ref ( ) . unwrap ( )
211
- }
212
-
213
228
pub fn all_buckets_empty ( & self ) -> bool {
214
229
self . work_buckets . values ( ) . all ( |bucket| bucket. is_empty ( ) )
215
230
}
@@ -221,7 +236,7 @@ impl<VM: VMBinding> GCWorkScheduler<VM> {
221
236
if id == WorkBucketStage :: Unconstrained {
222
237
continue ;
223
238
}
224
- buckets_updated |= bucket. update ( ) ;
239
+ buckets_updated |= bucket. update ( self ) ;
225
240
}
226
241
if buckets_updated {
227
242
// Notify the workers for new work
@@ -317,7 +332,7 @@ impl<VM: VMBinding> GCWorkScheduler<VM> {
317
332
}
318
333
// Park this worker
319
334
worker. shared . parked . store ( true , Ordering :: SeqCst ) ;
320
- if self . worker_group ( ) . all_parked ( ) {
335
+ if self . all_workers_parked ( ) {
321
336
worker
322
337
. sender
323
338
. send ( CoordinatorMessage :: AllWorkerParked )
@@ -331,33 +346,21 @@ impl<VM: VMBinding> GCWorkScheduler<VM> {
331
346
}
332
347
333
348
pub fn enable_stat ( & self ) {
334
- for worker in & self . worker_group ( ) . workers_shared {
349
+ for worker in & self . workers_shared {
335
350
let worker_stat = worker. borrow_stat ( ) ;
336
351
worker_stat. enable ( ) ;
337
352
}
338
- let coordinator_worker_shared = self
339
- . coordinator_worker_shared
340
- . as_ref ( )
341
- . unwrap ( )
342
- . read ( )
343
- . unwrap ( ) ;
344
- let coordinator_worker_stat = coordinator_worker_shared. borrow_stat ( ) ;
353
+ let coordinator_worker_stat = self . coordinator_worker_shared . borrow_stat ( ) ;
345
354
coordinator_worker_stat. enable ( ) ;
346
355
}
347
356
348
357
pub fn statistics ( & self ) -> HashMap < String , String > {
349
358
let mut summary = SchedulerStat :: default ( ) ;
350
- for worker in & self . worker_group ( ) . workers_shared {
359
+ for worker in & self . workers_shared {
351
360
let worker_stat = worker. borrow_stat ( ) ;
352
361
summary. merge ( & worker_stat) ;
353
362
}
354
- let coordinator_worker_shared = self
355
- . coordinator_worker_shared
356
- . as_ref ( )
357
- . unwrap ( )
358
- . read ( )
359
- . unwrap ( ) ;
360
- let coordinator_worker_stat = coordinator_worker_shared. borrow_stat ( ) ;
363
+ let coordinator_worker_stat = self . coordinator_worker_shared . borrow_stat ( ) ;
361
364
summary. merge ( & coordinator_worker_stat) ;
362
365
summary. harness_stat ( )
363
366
}
0 commit comments