about summary refs log tree commit diff stats
diff options
context:
space:
mode:
authorKartik K. Agaram <vc@akkartik.com>2016-01-21 04:35:39 -0800
committerKartik K. Agaram <vc@akkartik.com>2016-01-21 04:35:39 -0800
commit9b8d391799a096692b6dbb576d2863efb1ae8e6b (patch)
tree8a73ea54bc2f06864cd313e4e55b3ec417b135d6
parentb4cbb1d34b28909a938d8ac4ea614c416f9ce0d6 (diff)
downloadmu-9b8d391799a096692b6dbb576d2863efb1ae8e6b.tar.gz
2583 - start maintaining refcounts
Also start auto-abandoning addresses when their refcount returns to 0.

I'm mixing this auto-abandon support with my earlier/hackier support for
automatically abandoning default-space created by 'local-scope'. We need
to flesh out the story for automatically reclaiming memory using
C++-style destructors.

But that's a value-add. Memory corruption is far more important to avoid
than memory *leaks*.
-rw-r--r--020run.cc4
-rw-r--r--038new.cc67
2 files changed, 68 insertions, 3 deletions
diff --git a/020run.cc b/020run.cc
index 5ec96e67..19c508e8 100644
--- a/020run.cc
+++ b/020run.cc
@@ -273,12 +273,14 @@ void write_memory(reagent x, vector<double> data) {
   if (is_dummy(x)) return;
   if (is_literal(x)) return;
   long long int base = x.value;
+  if (base == 0) return;
   if (size_mismatch(x, data)) {
     raise_error << maybe(current_recipe_name()) << "size mismatch in storing to " << x.original_string << " (" << size_of(x.type) << " vs " << SIZE(data) << ") at '" << current_instruction().to_string() << "'\n" << end();
     return;
   }
+  // End write_memory(reagent x, long long int base) Special-cases
   for (long long int offset = 0; offset < SIZE(data); ++offset) {
-    if (base+offset == 0) continue;
+    assert(base+offset > 0);
     trace(9999, "mem") << "storing " << no_scientific(data.at(offset)) << " in location " << base+offset << end();
     put(Memory, base+offset, data.at(offset));
   }
diff --git a/038new.cc b/038new.cc
index b49e347e..88e5a515 100644
--- a/038new.cc
+++ b/038new.cc
@@ -261,7 +261,7 @@ recipe main [
 +new: routine allocated memory from 1000 to 1003
 +new: routine allocated memory from 1003 to 1006
 
-//: We also provide a way to return memory, and to reuse reclaimed memory.
+//:: A way to return memory, and to reuse reclaimed memory.
 //: todo: custodians, etc. Following malloc/free is a temporary hack.
 
 :(scenario new_reclaim)
@@ -368,7 +368,70 @@ recipe main [
 # reuse
 +mem: storing 1 in location 3
 
-//:: Next, extend 'new' to handle a unicode string literal argument.
+//:: Manage refcounts when copying addresses.
+
+:(scenario refcounts)
+recipe main [
+  1:address:shared:number <- copy 1000/unsafe
+  2:address:shared:number <- copy 1:address:shared:number
+  1:address:shared:number <- copy 0
+  2:address:shared:number <- copy 0
+]
++run: 1:address:shared:number <- copy 1000/unsafe
++mem: incrementing refcount of 1000: 0 -> 1
++run: 2:address:shared:number <- copy 1:address:shared:number
++mem: incrementing refcount of 1000: 1 -> 2
++run: 1:address:shared:number <- copy 0
++mem: decrementing refcount of 1000: 2 -> 1
++run: 2:address:shared:number <- copy 0
++mem: decrementing refcount of 1000: 1 -> 0
+# the /unsafe corrupts memory but fortunately we won't be running any more 'new' in this scenario
++mem: automatically abandoning 1000
+
+:(before "End write_memory(reagent x, long long int base) Special-cases")
+if (x.type->value == get(Type_ordinal, "address")
+    && x.type->right
+    && x.type->right->value == get(Type_ordinal, "shared")) {
+  // compute old address of x, as well as new address we want to write in
+  long long int old_address = get_or_insert(Memory, x.value);
+  assert(scalar(data));
+  long long int new_address = data.at(0);
+  // decrement refcount of old address
+  if (old_address) {
+    long long int old_refcount = get_or_insert(Memory, old_address);
+//?     cerr << old_refcount << '\n';
+//?     assert(old_refcount > 0);
+    trace(9999, "mem") << "decrementing refcount of " << old_address << ": " << old_refcount << " -> " << (old_refcount-1) << end();
+    put(Memory, old_address, old_refcount-1);
+  }
+  // perform the write
+  trace(9999, "mem") << "storing " << no_scientific(data.at(0)) << " in location " << base << end();
+  put(Memory, base, new_address);
+  // increment refcount of new address
+  if (new_address) {
+    long long int new_refcount = get_or_insert(Memory, new_address);
+//?       assert(new_refcount >= 0);  // == 0 only when new_address == old_address
+    trace(9999, "mem") << "incrementing refcount of " << new_address << ": " << new_refcount << " -> " << (new_refcount+1) << end();
+    put(Memory, new_address, new_refcount+1);
+  }
+  // abandon old address if necessary
+  // do this after all refcount updates are done just in case old and new are identical
+  // TODO: doesn't work yet
+  assert(get_or_insert(Memory, old_address) >= 0);
+  if (old_address && get_or_insert(Memory, old_address) == 0) {
+    // lookup_memory without drop_one_lookup {
+    trace(9999, "mem") << "automatically abandoning " << old_address << end();
+    trace(9999, "mem") << "computing size to abandon at " << x.value << end();
+    x.set_value(get_or_insert(Memory, x.value)+/*skip refcount*/1);
+    drop_from_type(x, "address");
+    drop_from_type(x, "shared");
+    // }
+    abandon(old_address, size_of(x)+/*refcount*/1);
+  }
+  return;
+}
+
+//:: Extend 'new' to handle a unicode string literal argument.
 
 :(scenario new_string)
 recipe main [