Skip to content

Commit c083e75

Browse files
committed
Refactor GC code so we can expand the heap on failure
1 parent ce27f09 commit c083e75

File tree

2 files changed

+110
-36
lines changed

2 files changed

+110
-36
lines changed

src/deepcopy.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -153,7 +153,7 @@ pub fn deepcopy(
153153
}
154154

155155
/// Remap internal references to copied values
156-
pub fn remap(dst_map: HashMap<Value, Value>)
156+
pub fn remap(dst_map: &mut HashMap<Value, Value>)
157157
{
158158
macro_rules! remap_val {
159159
($val: expr) => {

src/vm.rs

Lines changed: 109 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -733,7 +733,7 @@ impl Actor
733733
};
734734
let mut dst_map = HashMap::new();
735735
let msg = deepcopy(msg, alloc_rc.lock().as_mut().unwrap(), &mut dst_map).unwrap();
736-
remap(dst_map);
736+
remap(&mut dst_map);
737737

738738
match actor_tx.sender.send(Message { sender: self.actor_id, msg }) {
739739
Ok(_) => Ok(()),
@@ -851,53 +851,41 @@ impl Actor
851851
/// allocator. If the memory is not available, perform GC.
852852
pub fn gc_check(&mut self, num_bytes: usize, extra_roots: &mut [&mut Value])
853853
{
854-
if self.alloc.bytes_free() >= num_bytes {
855-
return;
856-
}
857-
858-
println!("Running GC cycle, {} bytes free", self.alloc.bytes_free());
859-
let start_time = crate::host::get_time_ms();
860-
861-
let mut new_mem_size = self.alloc.mem_size();
862-
863-
loop {
864-
// Create a new allocator to copy the data into
865-
let mut new_alloc = Alloc::with_size(new_mem_size);
866-
867-
// Hash map for remapping copied values
868-
let mut dst_map = HashMap::new();
869-
854+
fn try_copy(
855+
actor: &mut Actor,
856+
dst_alloc: &mut Alloc,
857+
dst_map: &mut HashMap<Value, Value>,
858+
extra_roots: &mut [&mut Value],
859+
) -> Result<(), ()>
860+
{
870861
// Copy the global variables
871-
for val in &mut self.globals {
872-
*val = deepcopy(*val, &mut new_alloc, &mut dst_map).unwrap();
862+
for val in &mut actor.globals {
863+
deepcopy(*val, dst_alloc, dst_map)?;
873864
}
874865

875866
// Copy values on the stack
876-
for val in &mut self.stack {
877-
*val = deepcopy(*val, &mut new_alloc, &mut dst_map).unwrap();
867+
for val in &mut actor.stack {
868+
deepcopy(*val, dst_alloc, dst_map)?;
878869
}
879870

880871
// Copy closures in the stack frames
881-
for frame in &mut self.frames {
882-
frame.fun = deepcopy(frame.fun, &mut new_alloc, &mut dst_map).unwrap();
872+
for frame in &mut actor.frames {
873+
deepcopy(frame.fun, dst_alloc, dst_map)?;
883874
}
884875

885876
// Copy heap values referenced in instructions
886-
for insn in &mut self.insns {
877+
for insn in &mut actor.insns {
887878
match insn {
888879
Insn::push { val } => {
889-
*val = deepcopy(*val, &mut new_alloc, &mut dst_map).unwrap();
880+
deepcopy(*val, dst_alloc, dst_map)?;
890881
}
891882

892883
// Instructions referencing name strings
893884
Insn::get_field { field: s, .. } |
894885
Insn::set_field { field: s, .. } |
895886
Insn::call_method { name: s, .. } |
896887
Insn::call_method_pc { name: s, .. } => {
897-
*s = match deepcopy(Value::String(*s), &mut new_alloc, &mut dst_map).unwrap() {
898-
Value::String(s) => s,
899-
_ => panic!(),
900-
}
888+
deepcopy(Value::String(*s), dst_alloc, dst_map)?;
901889
}
902890

903891
_ => {}
@@ -906,22 +894,106 @@ impl Actor
906894

907895
// Copy extra roots supplied by the user
908896
for val in extra_roots {
909-
**val = deepcopy(**val, &mut new_alloc, &mut dst_map).unwrap();
897+
deepcopy(**val, dst_alloc, dst_map)?;
910898
}
911899

912900
println!("GC copied {} values", dst_map.len());
913901
remap(dst_map);
914902

903+
Ok(())
904+
}
905+
906+
fn get_new_val(val: Value, dst_map: &HashMap<Value, Value>) -> Value
907+
{
908+
if !val.is_heap() {
909+
return val;
910+
}
911+
912+
*dst_map.get(&val).unwrap()
913+
}
914+
915+
if self.alloc.bytes_free() >= num_bytes {
916+
return;
917+
}
918+
919+
println!("Running GC cycle, {} bytes free", self.alloc.bytes_free());
920+
let start_time = crate::host::get_time_ms();
921+
922+
let mut new_mem_size = self.alloc.mem_size();
923+
924+
// Hash map for remapping copied values
925+
let mut dst_map = HashMap::<Value, Value>::new();
926+
927+
loop {
928+
// Create a new allocator to copy the data into
929+
let mut dst_alloc = Alloc::with_size(new_mem_size);
930+
931+
// Clear the value map
932+
dst_map.clear();
933+
934+
// Try to copy all objects into the new allocator
935+
if try_copy(self, &mut dst_alloc, &mut dst_map, extra_roots).is_err() {
936+
// If the copying fails, increase the heap size and try again
937+
new_mem_size = (new_mem_size * 5) / 4;
938+
println!("Increasing heap size to {} bytes", new_mem_size);
939+
continue;
940+
}
941+
942+
943+
// NOTE: we may want to run another GC cycle and expand the memory
944+
// if too little memory is free
945+
// Should have a target for something like 25% of memory free
946+
947+
948+
915949
// Copying successful
916950
// Drop and replace the old allocator
917-
self.alloc = new_alloc;
951+
self.alloc = dst_alloc;
918952
break;
919953
}
920954

955+
// Remap the global variables
956+
for val in &mut self.globals {
957+
*val = get_new_val(*val, &dst_map);
958+
}
959+
960+
// Remap values on the stack
961+
for val in &mut self.stack {
962+
*val = get_new_val(*val, &dst_map);
963+
}
964+
965+
// Remap closures in the stack frames
966+
for frame in &mut self.frames {
967+
frame.fun = get_new_val(frame.fun, &dst_map);
968+
}
969+
970+
// Remap heap values referenced in instructions
971+
for insn in &mut self.insns {
972+
match insn {
973+
Insn::push { val } => {
974+
*val = get_new_val(*val, &dst_map);
975+
}
976+
977+
// Instructions referencing name strings
978+
Insn::get_field { field: s, .. } |
979+
Insn::set_field { field: s, .. } |
980+
Insn::call_method { name: s, .. } |
981+
Insn::call_method_pc { name: s, .. } => {
982+
match get_new_val(Value::String(*s), &dst_map) {
983+
Value::String(new_s) => *s = new_s,
984+
_ => panic!(),
985+
}
986+
}
987+
988+
_ => {}
989+
}
990+
}
991+
992+
// Remap extra roots supplied by the user
993+
for val in extra_roots {
994+
**val = get_new_val(**val, &dst_map);
995+
}
921996

922-
// NOTE: we may want to run another GC cycle and expand the memory
923-
// if too little memory is free
924-
// Should have a target for something like 25% of memory free
925997

926998

927999

@@ -936,6 +1008,8 @@ impl Actor
9361008

9371009

9381010

1011+
1012+
9391013
let end_time = crate::host::get_time_ms();
9401014
let gc_time = end_time - start_time;
9411015
println!("GC time: {} ms", gc_time);
@@ -2177,7 +2251,7 @@ impl VM
21772251
*val = deepcopy(*val, &mut msg_alloc, &mut dst_map).unwrap();
21782252
}
21792253

2180-
remap(dst_map);
2254+
remap(&mut dst_map);
21812255

21822256
// Wrap the message allocator in a shared mutex
21832257
let msg_alloc = Arc::new(Mutex::new(msg_alloc));

0 commit comments

Comments
 (0)