-
Notifications
You must be signed in to change notification settings - Fork 9
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
[ospp][wip] Use the new weak reference processing API for the JikesRVM binding #145
Comments
week 1shorthas successfully built the project on a pure ubuntu:22.04 docker image with the ci script
detailthe ci script uses the GitHub action image, which has many installed dependencies. since jikesRVM originally support a 32-bit platform, there we have to add the i386 toolchain in apt. below is a partial from my bash history, I install the dependency one by one. say one error exists and then solve one error.
take a quick glance in ci script: update v1: next weekfamiliar codebase, from jikesRVM register a weakreference to update v2 && v3: my building script with docker image ubuntu 22.10 |
week 2shortfor jikesRVM original code base, I focus on the previous one called MemoryManager.java, and ReferenceProcessor.java detailthose codes are the primary entry the jikesRVM delegate the gc work to mmtk framework. the implementation is directly wording side. truly I found the most useful doc is jikesrvm.org/UserGuide/, and jikesRVM originally did much work in spec, like next week planWill start the first trial to remove the old reference processing. Currently, the binding for jikesRVM the reference processing will dump to the rust side process, we want to bypass this way and use the traditional and formal work in the original jikesRVM codebase. so, my basic idea is to set some code block to the |
Update v1: The original report has been edited to improve grammar and make the language more formal. week 3shortThe method mentioned last week was attempted but did not yield noticeable progress. detailI tried implementing the necessary methods with the next week planI'll first work on // Should finalization be disabled?
no_finalizer: bool [env_var: true, command_line: true] [always_valid] = false,
// Should reference type processing be disabled?
// If reference type processing is disabled, no weak reference processing work is scheduled,
// and we expect a binding to treat weak references as strong references.
// We disable weak reference processing by default, as we are still working on it. This will be changed to `false`
// once weak reference processing is implemented properly.
no_reference_types: bool [env_var: true, command_line: true] [always_valid] = true, |
Week 4ShortI have verified the current option for reference and finalizer processing and understand the design of the DetailPreviously, when I heard that we wanted to directly call the jikesRVM processing function, JNI and FFI were the first things that came to mind. However, even JNI introduces a significant overhead. After investigating the The fn schedule_finalization(tls: VMWorkerThread) {
unsafe {
jtoc_call!(SCHEDULE_FINALIZER_METHOD_OFFSET, tls);
}
} The To register a new method, you need to add a new item with the specific name in In
Next week's planIn our future expectations, the finalizer should be considered the same as a reference type for processing. I will copy the following code: fn process_weak_refs(
_worker: &mut GCWorker<VM>,
_tracer_context: impl ObjectTracerContext<VM>,
) -> bool {
false
} and register the necessary function in jikesRVM through the |
sorry for the late report. The best time to report is after the weekly sync meeting. Week 5Short Summary
DetailsInvestigated the call chain for finalizing the processorI previously added a throw backtrace on the Java side, and further, I noticed that we can simply use the IDE to search function symbols like // repos/jikesrvm/rvm/src/org/jikesrvm/runtime/SysCall.java:428
@Inline
public void sysAddFinalizer(Object object) {
add_finalizer(ObjectReference.fromObject(object));
}
@Inline
public Object sysGetFinalizedObject() {
return get_finalized_object().toObject();
} Experiments to verify GC mechanismBasically, finalization means the object is no longer reachable by any mutator. The below Click meimport java.lang.ref.SoftReference;
public class SoftReferenceTest {
private static class BigObject {
byte[] memoryEater = new byte[10 * 1024 * 1024]; // 10 MB
@Override
protected void finalize() throws Throwable {
super.finalize();
System.out.println("BigObject has been finalized");
}
}
public static void main(String[] args) {
BigObject obj = new BigObject();
SoftReference<BigObject> softRef = new SoftReference<BigObject>(obj);
obj = null;
int counter = 0;
// soft ref
while (softRef.get() != null) {
System.out.println("Trying to create another BigObject: " + (++counter));
try {
BigObject anotherBigObject = new BigObject();
} catch (OutOfMemoryError e) {
System.out.println("OutOfMemoryError thrown!");
break;
}
}
System.out.println("Soft reference has been cleared after " + counter + " BigObject allocations");
}
} Next week plansas talking previously, I will remove the rust route code and say we have to handle the relation, if (hasFinalizer) {
-- if (VM.BuildWithRustMMTk) {
-- sysCall.sysAddFinalizer(newObj);
-- } else {
MemoryManager.addFinalizer(newObj);
}
} the whole logic is Click me// mmtk-core/src/scheduler/gc_work.rs:392
impl<E: ProcessEdgesWork> GCWork<E::VM> for VMProcessWeakRefs<E> {
fn do_work(&mut self, worker: &mut GCWorker<E::VM>, _mmtk: &'static MMTK<E::VM>) {
trace!("VMProcessWeakRefs");
let stage = WorkBucketStage::VMRefClosure;
let need_to_repeat = {
let tracer_factory = ProcessEdgesWorkTracerContext::<E> {
stage,
phantom_data: PhantomData,
};
<E::VM as VMBinding>::VMScanning::process_weak_refs(worker, tracer_factory) <== here call our impl
};
if need_to_repeat {
// Schedule Self as the new sentinel so we'll call `process_weak_refs` again after the
// current transitive closure.
let new_self = Box::new(Self::new());
worker.scheduler().work_buckets[stage].set_sentinel(new_self);
}
}
}
// mmtk/src/scanning.rs:62
impl Scanning<JikesRVM> for VMScanning {
....
fn process_weak_refs(
_worker: &mut GCWorker<JikesRVM>, // worker -> thread
_tracer_context: impl ObjectTracerContext<JikesRVM>,
) -> bool {
->
{
rust side -> call java side <===== here we jump to java side process
object {finalizablprocessor.scan()} <=== This relies on TraceLocal. we intend a conversion internally in TraceLocal.
// also, need to check the global instance finalize processor whether has been registered.
}
false
}
} |
Week 6Short SummaryCompleted integration of jtoc_call and RustSyscall interactions. DetailsFor adding a jtoc_call:We need to register a method in
The jtoc_call accepts For adding a RustSyscall:Same as above, but needs extra
Next Week PlansAs previously discussed, gcwork will call |
Week 7ShortI have attempted to implement Java and Rust interoperability for call interaction, but the API design needs revisiting. DetailedThe Current:
Proposed change: ++ interface FinalizableProcessorTracer {
++ boolean isLive(ObjectReference object); // weak ref-> live dead undeterminde -> reachable
++ ObjectReference getForwardedFinalizable(ObjectReference object);
++ ObjectReference retainForFinalize(ObjectReference object);
++ }
public abstract class TraceLocal extends TransitiveClosure implements FinalizableProcessorTracer {
public final class RustTraceLocal implements FinalizableProcessorTracer { I now need to implement the 3 API usages in jikesRVM. More specifically, I need to call Rust and return to Java safely and correctly. Next Week PlanImplement the 3 calls and complete basic verification tests. |
Week 8This week I am busy with my work. Working overtime in the office to deliver an urgent product to the customer :< |
Week 9ShortThis week, I have done pretests from my mentor which are intended to help me implement pointer conversion without losing type information. I also took some time to review the PR https://github.com/mmtk/mmtk-jikesrvm/pull/114/files. DetailedIn our expected scenario, we currently have to implement Below is a snippet from the repo: https://github.com/fepicture/c-rust // https://github.com/fepicture/c-rust/blob/3d97607325b35820593aed64243b32a874c3e778/src/main.rs#L38-L55
extern "C" fn closure_internal<F: FnMut(i32)>(num: i32, closure: *mut c_void) {
let closure: &mut F = unsafe { &mut *(closure as *mut F) };
closure(num); // here do the closure, this is same in sysDynamicCall
}
extern "C" fn for_each_number<F: FnMut(i32)>(mut closure: F) {
let closure_ptr: *mut c_void = &mut closure as *mut F as *mut c_void;
unsafe { for_each_interesting_number(closure_internal::<F>, closure_ptr) };
}
fn pretest3_callback_from_callback() {
println!("\nbelow is pretest3_callback_from_callback");
for_each_number(|num| {
println!("Received number: {}", num);
});
} next week planTake more time in finalizer, and try to finish it ASAP |
Week 10ShortI've completed the first take on the finalizer replacement. It currently passes my simple test, but I still have some questions. DetailedAfter conducting the pretest as previously discussed, I gained a deeper understanding of type conversion. Following the steps mentioned above, I utilized // /root/.cargo/git/checkouts/mmtk-core-3306bdeb8eb4322b/269408b/src/vm/scanning.rs:50
pub trait ObjectTracerContext<VM: VMBinding>: Clone + Send + 'static {
/// The concrete `ObjectTracer` type.
///
/// FIXME: The current code functions due to the unsafe method `ProcessEdgesWork::set_worker`.
/// The tracer should borrow the worker passed to `with_queuing_tracer` during its lifetime.
/// For this reason, `TracerType` should have a `<'w>` lifetime parameter.
/// Generic Associated Types (GAT) was stabilized in Rust 1.65.
/// We should consider updating our toolchain version.
type TracerType: ObjectTracer; Currently, I am passing the type info into another function outside of the implementation, as shown below: // https://github.com/fepicture/mmtk-jikesrvm/blob/ee1e732f47c74520d313dd6777ba898e16881d2c/mmtk/src/scanning.rs#L182-L207
impl Scanning<JikesRVM> for VMScanning {
fn process_weak_refs(
_worker: &mut GCWorker<JikesRVM>,
_tracer_context: impl ObjectTracerContext<JikesRVM>,
) -> bool {
process_weak_refs_inner(_worker, _tracer_context)
}
}
fn process_weak_refs_inner<C>(_worker: &mut GCWorker<JikesRVM>, _tracer_context: C) -> bool
where
C: ObjectTracerContext<JikesRVM>,
{
let tls = _worker.tls;
_tracer_context.with_tracer(_worker, |tracer| {
unsafe {
jtoc_call!(
DO_FINALIZABLE_PROCESSOR_SCAN_METHOD_OFFSET,
tls,
trace_object_callback_for_jikesrvm::<C::TracerType>,
tracer as *mut _ as *mut libc::c_void,
0
);
}
});
false
} Next WeekI'll investigate the cause of this semispace error report and address the issue with the nursery (currently, we're passing |
update v1: add Week 11ShortI resolved last week's error by increasing the maximum heap allocation ( DetailedWhile running the test suite in Regarding the merger of functionalities, I examined JikesRVM's handling of the reference processor more closely. The three types of references are founded on the same class, necessitating a state machine-like approach to navigate through the four types based on their reference strength. This can be executed either on the Java side or on the Rust side. private static final ReferenceProcessor softReferenceProcessor =
new ReferenceProcessor(Semantics.SOFT);
private static final ReferenceProcessor weakReferenceProcessor =
new ReferenceProcessor(Semantics.WEAK);
private static final ReferenceProcessor phantomReferenceProcessor =
new ReferenceProcessor(Semantics.PHANTOM); To meet the requirements of public interface FinalizableProcessorTracer {
boolean isLive(ObjectReference object); // From weak ref -> live/dead undetermined -> reachable
ObjectReference getForwardedFinalizable(ObjectReference object);
ObjectReference getForwardedReferent(ObjectReference object);
ObjectReference getForwardedReference(ObjectReference object);
ObjectReference retainReferent(ObjectReference object);
ObjectReference retainForFinalize(ObjectReference object);
} Plans for Next WeekI aim to continue working on integrating the four types of references. mmtk
.get_plan()
.generational()
.unwrap()
.is_current_gc_nursery()
fn is_emergency_collection(&self) -> bool {
self.base().emergency_collection.load(Ordering::Relaxed)
}
|
Week 12Brief OverviewPresently, the PR is ready for review. I have developed a Detailed Explanationmmtk-jikesrvmModifications to the mmtk-jikesrvm repository encompass three key aspects:
jikesrvmThe changes in the jikesrvm repository are somewhat more extensive. To summarize, we initiate from the Rust MMTk side entry, which proceeds into a static method by // mmtk/src/scanning.rs:220
tracer_context.with_tracer(worker, |tracer| unsafe {
scan_result = jtoc_call!(
DO_REFERENCE_PROCESSOR_DELEGATOR_SCAN_METHOD_OFFSET,
tls,
trace_object_callback_for_jikesrvm::<C::TracerType>,
tracer as *mut _ as *mut libc::c_void,
is_nursery as i32,
need_retain as i32
);
}); // rvm/src/org/jikesrvm/mm/mminterface/MemoryManager.java:1208
@Entrypoint
public static boolean doReferenceProcessorDelegatorScan(Address traceObjectCallback, Address tracer, boolean isNursery, boolean needRetain) {
return org.mmtk.vm.VM.referenceProcessorDelegator.scan(traceObjectCallback, tracer, isNursery, needRetain);
} // MMTk/ext/vm/jikesrvm/org/jikesrvm/mm/mmtk/ReferenceProcessorDelegator.java:88
@Override
@UninterruptibleNoWarn
public boolean scan(Address traceObjectCallback, Address tracer, boolean isNursery, boolean needRetain) {
RustTraceLocal trace = new RustTraceLocal(traceObjectCallback, tracer);
if (phaseId == Phase.PREPARE) {
phaseId = Phase.SOFT_REFS;
}
if (phaseId == Phase.SOFT_REFS) {
org.mmtk.vm.VM.softReferences.scan(trace, isNursery, needRetain);
phaseId = Phase.WEAK_REFS;
return true;
} else if (phaseId == Phase.WEAK_REFS) {
org.mmtk.vm.VM.weakReferences.scan(trace, isNursery, needRetain);
phaseId = Phase.FINALIZABLE;
return true;
} else if (phaseId == Phase.FINALIZABLE) {
org.mmtk.vm.VM.finalizableProcessor.scan(trace, isNursery);
phaseId = Phase.PHANTOM_REFS;
return true;
} else if (phaseId == Phase.PHANTOM_REFS) {
org.mmtk.vm.VM.phantomReferences.scan(trace, isNursery, needRetain);
phaseId = Phase.PREPARE;
return false;
} else {
VM.sysFail("unreachable");
return false;
}
} |
_edit v1: sync latest status, i have finish the reference processor and integrate together._ _edit v2: update with binding PR link._ fix #145 mmtk-jikesrvm PR #150 jikesRVM PR mmtk/jikesrvm#16 Modifications to the mmtk-jikesrvm repository encompass three key aspects: 1. I have removed some old bind APIs, marking them as unimplement, or directly removing them. 2. I implemented the process_weak_ref and forward_weak_ref methods. These will be invoked in the work bucket and act as an entry point for the original process in jikesrvm. 3. Lastly, the CI heap configuration has been updated. --------- Co-authored-by: Yi Lin <[email protected]> Co-authored-by: Kunshan Wang <[email protected]>
this issue is tracking the weekly update on this project. also, mmtk@zulipchat has a sub-channel for this project discussion.
Motivation
shortly, porting the java-style jikesRVM to rust-lang is straightforward, but the reference processing is java-specific.
say, we expected a more general reference processing if we want to move forward to another language or gc mechanism.
Milestone
reference
#137
mmtk/mmtk-core#694
https://summer-ospp.ac.cn/org/prodetail/235730136
The text was updated successfully, but these errors were encountered: