1use super::stat::WorkerLocalStat;
2use super::work_bucket::*;
3use super::*;
4use crate::mmtk::MMTK;
5use crate::util::copy::GCWorkerCopyContext;
6use crate::util::heap::layout::heap_parameters::MAX_SPACES;
7use crate::util::opaque_pointer::*;
8use crate::util::ObjectReference;
9use crate::vm::{Collection, GCThreadContext, VMBinding};
10use atomic::Atomic;
11use atomic_refcell::{AtomicRef, AtomicRefCell, AtomicRefMut};
12use crossbeam::deque::{self, Stealer};
13use crossbeam::queue::ArrayQueue;
14use std::sync::atomic::Ordering;
15use std::sync::{Arc, Mutex};
16
17pub type ThreadId = usize;
19
20thread_local! {
21 static WORKER_ORDINAL: Atomic<ThreadId> = const { Atomic::new(ThreadId::MAX) };
23}
24
25pub fn current_worker_ordinal() -> Option<ThreadId> {
27 let ordinal = WORKER_ORDINAL.with(|x| x.load(Ordering::Relaxed));
28 if ordinal == ThreadId::MAX {
29 None
30 } else {
31 Some(ordinal)
32 }
33}
34
35pub struct GCWorkerShared<VM: VMBinding> {
39 stat: AtomicRefCell<WorkerLocalStat<VM>>,
41 pub live_bytes_per_space: AtomicRefCell<[usize; MAX_SPACES]>,
46 pub designated_work: ArrayQueue<Box<dyn GCWork<VM>>>,
48 pub stealer: Option<Stealer<Box<dyn GCWork<VM>>>>,
50}
51
52impl<VM: VMBinding> GCWorkerShared<VM> {
53 pub fn new(stealer: Option<Stealer<Box<dyn GCWork<VM>>>>) -> Self {
56 Self {
57 stat: Default::default(),
58 live_bytes_per_space: AtomicRefCell::new([0; MAX_SPACES]),
59 designated_work: ArrayQueue::new(16),
60 stealer,
61 }
62 }
63
64 pub(crate) fn increase_live_bytes(
65 live_bytes_per_space: &mut [usize; MAX_SPACES],
66 object: ObjectReference,
67 ) {
68 use crate::mmtk::VM_MAP;
69 use crate::vm::object_model::ObjectModel;
70
71 let bytes = VM::VMObjectModel::get_current_size(object);
73 let space_descriptor = VM_MAP.get_descriptor_for_address(object.to_raw_address());
75 if space_descriptor != crate::util::heap::space_descriptor::SpaceDescriptor::UNINITIALIZED {
76 let space_index = space_descriptor.get_index();
77 debug_assert!(
78 space_index < MAX_SPACES,
79 "Space index {} is not in the range of [0, {})",
80 space_index,
81 MAX_SPACES
82 );
83 live_bytes_per_space[space_index] += bytes;
85 }
86 }
87}
88
89pub struct GCWorker<VM: VMBinding> {
91 pub tls: VMWorkerThread,
93 pub ordinal: ThreadId,
95 scheduler: Arc<GCWorkScheduler<VM>>,
97 copy: GCWorkerCopyContext<VM>,
99 pub mmtk: &'static MMTK<VM>,
101 pub shared: Arc<GCWorkerShared<VM>>,
103 pub local_work_buffer: deque::Worker<Box<dyn GCWork<VM>>>,
105}
106
107unsafe impl<VM: VMBinding> Sync for GCWorkerShared<VM> {}
108unsafe impl<VM: VMBinding> Send for GCWorkerShared<VM> {}
109
110const STAT_BORROWED_MSG: &str = "GCWorkerShared.stat is already borrowed. This may happen if \
112 the mutator calls harness_begin or harness_end while the GC is running.";
113
114impl<VM: VMBinding> GCWorkerShared<VM> {
115 pub fn borrow_stat(&self) -> AtomicRef<'_, WorkerLocalStat<VM>> {
117 self.stat.try_borrow().expect(STAT_BORROWED_MSG)
118 }
119
120 pub fn borrow_stat_mut(&self) -> AtomicRefMut<'_, WorkerLocalStat<VM>> {
122 self.stat.try_borrow_mut().expect(STAT_BORROWED_MSG)
123 }
124}
125
126#[derive(Debug)]
129pub(crate) struct WorkerShouldExit;
130
131pub(crate) type PollResult<VM> = Result<Box<dyn GCWork<VM>>, WorkerShouldExit>;
136
137impl<VM: VMBinding> GCWorker<VM> {
138 pub(crate) fn new(
139 mmtk: &'static MMTK<VM>,
140 ordinal: ThreadId,
141 scheduler: Arc<GCWorkScheduler<VM>>,
142 shared: Arc<GCWorkerShared<VM>>,
143 local_work_buffer: deque::Worker<Box<dyn GCWork<VM>>>,
144 ) -> Self {
145 Self {
146 tls: VMWorkerThread(VMThread::UNINITIALIZED),
147 ordinal,
148 copy: GCWorkerCopyContext::new_non_copy(),
150 scheduler,
151 mmtk,
152 shared,
153 local_work_buffer,
154 }
155 }
156
157 const LOCALLY_CACHED_WORK_PACKETS: usize = 16;
158
159 pub fn add_boxed_work(&mut self, bucket: WorkBucketStage, work: Box<dyn GCWork<VM>>) {
162 if !self.scheduler().work_buckets[bucket].is_open()
163 || self.local_work_buffer.len() >= Self::LOCALLY_CACHED_WORK_PACKETS
164 {
165 self.scheduler.work_buckets[bucket].add_boxed(work);
166 return;
167 }
168 self.local_work_buffer.push(work);
169 }
170
171 pub fn add_work(&mut self, bucket: WorkBucketStage, work: impl GCWork<VM>) {
175 if !self.scheduler().work_buckets[bucket].is_open()
176 || self.local_work_buffer.len() >= Self::LOCALLY_CACHED_WORK_PACKETS
177 {
178 self.scheduler.work_buckets[bucket].add(work);
179 return;
180 }
181 self.local_work_buffer.push(Box::new(work));
182 }
183
184 pub fn scheduler(&self) -> &GCWorkScheduler<VM> {
186 &self.scheduler
187 }
188
189 pub fn get_copy_context_mut(&mut self) -> &mut GCWorkerCopyContext<VM> {
191 &mut self.copy
192 }
193
194 fn poll(&mut self) -> PollResult<VM> {
201 if let Some(work) = self.shared.designated_work.pop() {
202 return Ok(work);
203 }
204
205 if let Some(work) = self.local_work_buffer.pop() {
206 return Ok(work);
207 }
208
209 self.scheduler().poll(self)
210 }
211
212 pub fn run(mut self: Box<Self>, tls: VMWorkerThread, mmtk: &'static MMTK<VM>) {
224 probe!(mmtk, gcworker_run);
225 debug!(
226 "Worker started. ordinal: {}, {}",
227 self.ordinal,
228 crate::util::rust_util::debug_process_thread_id(),
229 );
230 WORKER_ORDINAL.with(|x| x.store(self.ordinal, Ordering::SeqCst));
231 self.scheduler.resolve_affinity(self.ordinal);
232 self.tls = tls;
233 self.copy = crate::plan::create_gc_worker_context(tls, mmtk);
234 loop {
235 probe!(mmtk, work_poll);
244 let Ok(mut work) = self.poll() else {
245 break;
247 };
248 #[allow(unused_variables)]
250 let typename = work.get_type_name();
251
252 #[cfg(feature = "bpftrace_workaround")]
253 std::hint::black_box(unsafe { *(typename.as_ptr()) });
257
258 probe!(mmtk, work, typename.as_ptr(), typename.len());
259 debug_assert!(
260 self.scheduler().is_worker_active(self.ordinal),
261 "Worker {} is executing {} while inactive.",
262 self.ordinal,
263 typename
264 );
265 work.do_work_with_stat(&mut self, mmtk);
266 }
267 debug!(
268 "Worker exiting. ordinal: {}, {}",
269 self.ordinal,
270 crate::util::rust_util::debug_process_thread_id(),
271 );
272 probe!(mmtk, gcworker_exit);
273
274 mmtk.scheduler.surrender_gc_worker(self);
275 }
276}
277
278enum WorkerCreationState<VM: VMBinding> {
280 Initial {
283 local_work_queues: Vec<deque::Worker<Box<dyn GCWork<VM>>>>,
285 },
286 Spawned,
289 Surrendered {
292 #[allow(clippy::vec_box)]
298 workers: Vec<Box<GCWorker<VM>>>,
299 },
300}
301
302pub(crate) struct WorkerGroup<VM: VMBinding> {
304 pub workers_shared: Vec<Arc<GCWorkerShared<VM>>>,
306 state: Mutex<Option<WorkerCreationState<VM>>>,
308}
309
310unsafe impl<VM: VMBinding> Sync for WorkerGroup<VM> {}
314
315impl<VM: VMBinding> WorkerGroup<VM> {
316 pub fn new(num_workers: usize) -> Arc<Self> {
318 let local_work_queues = (0..num_workers)
319 .map(|_| deque::Worker::new_fifo())
320 .collect::<Vec<_>>();
321
322 let workers_shared = (0..num_workers)
323 .map(|i| {
324 Arc::new(GCWorkerShared::<VM>::new(Some(
325 local_work_queues[i].stealer(),
326 )))
327 })
328 .collect::<Vec<_>>();
329
330 Arc::new(Self {
331 workers_shared,
332 state: Mutex::new(Some(WorkerCreationState::Initial { local_work_queues })),
333 })
334 }
335
336 pub fn initial_spawn(&self, tls: VMThread, mmtk: &'static MMTK<VM>) {
338 let mut state = self.state.lock().unwrap();
339
340 let WorkerCreationState::Initial { local_work_queues } = state.take().unwrap() else {
341 panic!("GCWorker structs have already been created");
342 };
343
344 let workers = self.create_workers(local_work_queues, mmtk);
345 self.spawn(workers, tls);
346
347 *state = Some(WorkerCreationState::Spawned);
348 }
349
350 pub fn respawn(&self, tls: VMThread) {
352 let mut state = self.state.lock().unwrap();
353
354 let WorkerCreationState::Surrendered { workers } = state.take().unwrap() else {
355 panic!("GCWorker structs have not been created, yet.");
356 };
357
358 self.spawn(workers, tls);
359
360 *state = Some(WorkerCreationState::Spawned)
361 }
362
363 #[allow(clippy::vec_box)] fn create_workers(
366 &self,
367 local_work_queues: Vec<deque::Worker<Box<dyn GCWork<VM>>>>,
368 mmtk: &'static MMTK<VM>,
369 ) -> Vec<Box<GCWorker<VM>>> {
370 debug!("Creating GCWorker instances...");
371
372 assert_eq!(self.workers_shared.len(), local_work_queues.len());
373
374 let workers = (local_work_queues.into_iter())
376 .zip(self.workers_shared.iter())
377 .enumerate()
378 .map(|(ordinal, (queue, shared))| {
379 Box::new(GCWorker::new(
380 mmtk,
381 ordinal,
382 mmtk.scheduler.clone(),
383 shared.clone(),
384 queue,
385 ))
386 })
387 .collect::<Vec<_>>();
388
389 debug!("Created {} GCWorker instances.", workers.len());
390 workers
391 }
392
393 #[allow(clippy::vec_box)] fn spawn(&self, workers: Vec<Box<GCWorker<VM>>>, tls: VMThread) {
396 debug!(
397 "Spawning GC workers. {}",
398 crate::util::rust_util::debug_process_thread_id(),
399 );
400
401 for worker in workers {
403 VM::VMCollection::spawn_gc_thread(tls, GCThreadContext::<VM>::Worker(worker));
404 }
405
406 debug!(
407 "Spawned {} worker threads. {}",
408 self.worker_count(),
409 crate::util::rust_util::debug_process_thread_id(),
410 );
411 }
412
413 pub fn prepare_surrender_buffer(&self) {
415 let mut state = self.state.lock().unwrap();
416 assert!(matches!(*state, Some(WorkerCreationState::Spawned)));
417
418 *state = Some(WorkerCreationState::Surrendered {
419 workers: Vec::with_capacity(self.worker_count()),
420 })
421 }
422
423 pub fn surrender_gc_worker(&self, worker: Box<GCWorker<VM>>) -> bool {
426 let mut state = self.state.lock().unwrap();
427 let WorkerCreationState::Surrendered { ref mut workers } = state.as_mut().unwrap() else {
428 panic!("GCWorker structs have not been created, yet.");
429 };
430 let ordinal = worker.ordinal;
431 workers.push(worker);
432 trace!(
433 "Worker {} surrendered. ({}/{})",
434 ordinal,
435 workers.len(),
436 self.worker_count()
437 );
438 workers.len() == self.worker_count()
439 }
440
441 pub fn worker_count(&self) -> usize {
443 self.workers_shared.len()
444 }
445
446 pub fn has_designated_work(&self) -> bool {
448 self.workers_shared
449 .iter()
450 .any(|w| !w.designated_work.is_empty())
451 }
452
453 pub fn get_and_clear_worker_live_bytes(&self) -> [usize; MAX_SPACES] {
455 let mut ret = [0; MAX_SPACES];
456 self.workers_shared.iter().for_each(|w| {
457 let mut live_bytes_per_space = w.live_bytes_per_space.borrow_mut();
458 for (idx, val) in live_bytes_per_space.iter_mut().enumerate() {
459 ret[idx] += *val;
460 *val = 0;
461 }
462 });
463 ret
464 }
465}