@@ -108,9 +108,9 @@ impl<C: GCWorkContext> Release<C> {
108
108
impl < C : GCWorkContext + ' static > GCWork < C :: VM > for Release < C > {
109
109
fn do_work ( & mut self , worker : & mut GCWorker < C :: VM > , mmtk : & ' static MMTK < C :: VM > ) {
110
110
trace ! ( "Release Global" ) ;
111
+
111
112
self . plan . base ( ) . gc_trigger . policy . on_gc_release ( mmtk) ;
112
113
113
- <C :: VM as VMBinding >:: VMCollection :: vm_release ( ) ;
114
114
// We assume this is the only running work packet that accesses plan at the point of execution
115
115
#[ allow( clippy:: cast_ref_to_mut) ]
116
116
let plan_mut: & mut C :: PlanType = unsafe { & mut * ( self . plan as * const _ as * mut _ ) } ;
@@ -252,24 +252,190 @@ impl<VM: VMBinding> GCWork<VM> for EndOfGC {
252
252
253
253
impl < VM : VMBinding > CoordinatorWork < VM > for EndOfGC { }
254
254
255
- /// Delegate to the VM binding for reference processing.
255
+ /// This implements `ObjectTracer` by forwarding the `trace_object` calls to the wrapped
256
+ /// `ProcessEdgesWork` instance.
257
+ struct ProcessEdgesWorkTracer < E : ProcessEdgesWork > {
258
+ process_edges_work : E ,
259
+ stage : WorkBucketStage ,
260
+ }
261
+
262
+ impl < E : ProcessEdgesWork > ObjectTracer for ProcessEdgesWorkTracer < E > {
263
+ /// Forward the `trace_object` call to the underlying `ProcessEdgesWork`,
264
+ /// and flush as soon as the underlying buffer of `process_edges_work` is full.
265
+ ///
266
+ /// This function is inlined because `trace_object` is probably the hottest function in MMTk.
267
+ /// If this function is called in small closures, please profile the program and make sure the
268
+ /// closure is inlined, too.
269
+ #[ inline( always) ]
270
+ fn trace_object ( & mut self , object : ObjectReference ) -> ObjectReference {
271
+ let result = self . process_edges_work . trace_object ( object) ;
272
+ self . flush_if_full ( ) ;
273
+ result
274
+ }
275
+ }
276
+
277
+ impl < E : ProcessEdgesWork > ProcessEdgesWorkTracer < E > {
278
+ #[ inline( always) ]
279
+ fn flush_if_full ( & mut self ) {
280
+ if self . process_edges_work . nodes . is_full ( ) {
281
+ self . flush ( ) ;
282
+ }
283
+ }
284
+
285
+ pub fn flush_if_not_empty ( & mut self ) {
286
+ if !self . process_edges_work . nodes . is_empty ( ) {
287
+ self . flush ( ) ;
288
+ }
289
+ }
290
+
291
+ #[ cold]
292
+ fn flush ( & mut self ) {
293
+ let next_nodes = self . process_edges_work . pop_nodes ( ) ;
294
+ assert ! ( !next_nodes. is_empty( ) ) ;
295
+ let work_packet = self . process_edges_work . create_scan_work ( next_nodes, false ) ;
296
+ let worker = self . process_edges_work . worker ( ) ;
297
+ worker. scheduler ( ) . work_buckets [ self . stage ] . add ( work_packet) ;
298
+ }
299
+ }
300
+
301
+ /// This type implements `ObjectTracerContext` by creating a temporary `ProcessEdgesWork` during
302
+ /// the call to `with_tracer`, making use of its `trace_object` method. It then creates work
303
+ /// packets using the methods of the `ProcessEdgesWork` and add the work packet into the given
304
+ /// `stage`.
305
+ struct ProcessEdgesWorkTracerContext < E : ProcessEdgesWork > {
306
+ stage : WorkBucketStage ,
307
+ phantom_data : PhantomData < E > ,
308
+ }
309
+
310
+ impl < E : ProcessEdgesWork > Clone for ProcessEdgesWorkTracerContext < E > {
311
+ fn clone ( & self ) -> Self {
312
+ Self { ..* self }
313
+ }
314
+ }
315
+
316
+ impl < E : ProcessEdgesWork > ObjectTracerContext < E :: VM > for ProcessEdgesWorkTracerContext < E > {
317
+ type TracerType = ProcessEdgesWorkTracer < E > ;
318
+
319
+ fn with_tracer < R , F > ( & self , worker : & mut GCWorker < E :: VM > , func : F ) -> R
320
+ where
321
+ F : FnOnce ( & mut Self :: TracerType ) -> R ,
322
+ {
323
+ let mmtk = worker. mmtk ;
324
+
325
+ // Prepare the underlying ProcessEdgesWork
326
+ let mut process_edges_work = E :: new ( vec ! [ ] , false , mmtk) ;
327
+ // FIXME: This line allows us to omit the borrowing lifetime of worker.
328
+ // We should refactor ProcessEdgesWork so that it uses `worker` locally, not as a member.
329
+ process_edges_work. set_worker ( worker) ;
330
+
331
+ // Cretae the tracer.
332
+ let mut tracer = ProcessEdgesWorkTracer {
333
+ process_edges_work,
334
+ stage : self . stage ,
335
+ } ;
336
+
337
+ // The caller can use the tracer here.
338
+ let result = func ( & mut tracer) ;
339
+
340
+ // Flush the queued nodes.
341
+ tracer. flush_if_not_empty ( ) ;
342
+
343
+ result
344
+ }
345
+ }
346
+
347
+ /// Delegate to the VM binding for weak reference processing.
256
348
///
257
349
/// Some VMs (e.g. v8) do not have a Java-like global weak reference storage, and the
258
350
/// processing of those weakrefs may be more complex. For such case, we delegate to the
259
351
/// VM binding to process weak references.
260
- #[ derive( Default ) ]
261
- pub struct VMProcessWeakRefs < E : ProcessEdgesWork > ( PhantomData < E > ) ;
352
+ ///
353
+ /// NOTE: This will replace `{Soft,Weak,Phantom}RefProcessing` and `Finalization` in the future.
354
+ pub struct VMProcessWeakRefs < E : ProcessEdgesWork > {
355
+ phantom_data : PhantomData < E > ,
356
+ }
262
357
263
358
impl < E : ProcessEdgesWork > VMProcessWeakRefs < E > {
264
359
pub fn new ( ) -> Self {
265
- Self ( PhantomData )
360
+ Self {
361
+ phantom_data : PhantomData ,
362
+ }
266
363
}
267
364
}
268
365
269
366
impl < E : ProcessEdgesWork > GCWork < E :: VM > for VMProcessWeakRefs < E > {
270
367
fn do_work ( & mut self , worker : & mut GCWorker < E :: VM > , _mmtk : & ' static MMTK < E :: VM > ) {
271
- trace ! ( "ProcessWeakRefs" ) ;
272
- <E :: VM as VMBinding >:: VMCollection :: process_weak_refs ( worker) ; // TODO: Pass a factory/callback to decide what work packet to create.
368
+ trace ! ( "VMProcessWeakRefs" ) ;
369
+
370
+ let stage = WorkBucketStage :: VMRefClosure ;
371
+
372
+ let need_to_repeat = {
373
+ let tracer_factory = ProcessEdgesWorkTracerContext :: < E > {
374
+ stage,
375
+ phantom_data : PhantomData ,
376
+ } ;
377
+ <E :: VM as VMBinding >:: VMScanning :: process_weak_refs ( worker, tracer_factory)
378
+ } ;
379
+
380
+ if need_to_repeat {
381
+ // Schedule Self as the new sentinel so we'll call `process_weak_refs` again after the
382
+ // current transitive closure.
383
+ let new_self = Box :: new ( Self :: new ( ) ) ;
384
+
385
+ worker. scheduler ( ) . work_buckets [ stage] . set_sentinel ( new_self) ;
386
+ }
387
+ }
388
+ }
389
+
390
+ /// Delegate to the VM binding for forwarding weak references.
391
+ ///
392
+ /// Some VMs (e.g. v8) do not have a Java-like global weak reference storage, and the
393
+ /// processing of those weakrefs may be more complex. For such case, we delegate to the
394
+ /// VM binding to process weak references.
395
+ ///
396
+ /// NOTE: This will replace `RefForwarding` and `ForwardFinalization` in the future.
397
+ pub struct VMForwardWeakRefs < E : ProcessEdgesWork > {
398
+ phantom_data : PhantomData < E > ,
399
+ }
400
+
401
+ impl < E : ProcessEdgesWork > VMForwardWeakRefs < E > {
402
+ pub fn new ( ) -> Self {
403
+ Self {
404
+ phantom_data : PhantomData ,
405
+ }
406
+ }
407
+ }
408
+
409
+ impl < E : ProcessEdgesWork > GCWork < E :: VM > for VMForwardWeakRefs < E > {
410
+ fn do_work ( & mut self , worker : & mut GCWorker < E :: VM > , _mmtk : & ' static MMTK < E :: VM > ) {
411
+ trace ! ( "VMForwardWeakRefs" ) ;
412
+
413
+ let stage = WorkBucketStage :: VMRefForwarding ;
414
+
415
+ let tracer_factory = ProcessEdgesWorkTracerContext :: < E > {
416
+ stage,
417
+ phantom_data : PhantomData ,
418
+ } ;
419
+ <E :: VM as VMBinding >:: VMScanning :: forward_weak_refs ( worker, tracer_factory)
420
+ }
421
+ }
422
+
423
+ /// This work packet calls `Collection::post_forwarding`.
424
+ ///
425
+ /// NOTE: This will replace `RefEnqueue` in the future.
426
+ ///
427
+ /// NOTE: Although this work packet runs in parallel with the `Release` work packet, it does not
428
+ /// access the `Plan` instance.
429
+ #[ derive( Default ) ]
430
+ pub struct VMPostForwarding < VM : VMBinding > {
431
+ phantom_data : PhantomData < VM > ,
432
+ }
433
+
434
+ impl < VM : VMBinding > GCWork < VM > for VMPostForwarding < VM > {
435
+ fn do_work ( & mut self , worker : & mut GCWorker < VM > , _mmtk : & ' static MMTK < VM > ) {
436
+ trace ! ( "VMPostForwarding start" ) ;
437
+ <VM as VMBinding >:: VMCollection :: post_forwarding ( worker. tls ) ;
438
+ trace ! ( "VMPostForwarding end" ) ;
273
439
}
274
440
}
275
441
@@ -678,38 +844,22 @@ pub trait ScanObjectsWork<VM: VMBinding>: GCWork<VM> + Sized {
678
844
679
845
// If any object does not support edge-enqueuing, we process them now.
680
846
if !scan_later. is_empty ( ) {
681
- // We create an instance of E to use its `trace_object` method and its object queue.
682
- let mut process_edges_work = Self :: E :: new ( vec ! [ ] , false , mmtk) ;
683
- let mut closure = |object| process_edges_work. trace_object ( object) ;
684
-
685
- // Scan objects and trace their edges at the same time.
686
- for object in scan_later. iter ( ) . copied ( ) {
687
- <VM as VMBinding >:: VMScanning :: scan_object_and_trace_edges (
688
- tls,
689
- object,
690
- & mut closure,
691
- ) ;
692
- self . post_scan_object ( object) ;
693
- }
694
-
695
- // Create work packets to scan adjacent objects. We skip ProcessEdgesWork and create
696
- // object-scanning packets directly, because the edges are already traced.
697
- if !process_edges_work. nodes . is_empty ( ) {
698
- let next_nodes = process_edges_work. nodes . take ( ) ;
699
- let make_packet = |nodes| {
700
- let work_packet = self . make_another ( nodes) ;
701
- memory_manager:: add_work_packet ( mmtk, WorkBucketStage :: Closure , work_packet) ;
702
- } ;
703
-
704
- // Divide the resulting nodes into appropriately sized packets.
705
- if next_nodes. len ( ) <= Self :: E :: CAPACITY {
706
- make_packet ( next_nodes) ;
707
- } else {
708
- for chunk in next_nodes. chunks ( Self :: E :: CAPACITY ) {
709
- make_packet ( chunk. into ( ) ) ;
710
- }
847
+ let object_tracer_context = ProcessEdgesWorkTracerContext :: < Self :: E > {
848
+ stage : WorkBucketStage :: Closure ,
849
+ phantom_data : PhantomData ,
850
+ } ;
851
+
852
+ object_tracer_context. with_tracer ( worker, |object_tracer| {
853
+ // Scan objects and trace their edges at the same time.
854
+ for object in scan_later. iter ( ) . copied ( ) {
855
+ <VM as VMBinding >:: VMScanning :: scan_object_and_trace_edges (
856
+ tls,
857
+ object,
858
+ object_tracer,
859
+ ) ;
860
+ self . post_scan_object ( object) ;
711
861
}
712
- }
862
+ } ) ;
713
863
}
714
864
}
715
865
}
0 commit comments