about summary refs log tree commit diff stats
diff options
context:
space:
mode:
authorKartik K. Agaram <vc@akkartik.com>2017-11-19 04:23:31 -0800
committerKartik K. Agaram <vc@akkartik.com>2017-11-19 04:23:31 -0800
commit87374384552f159713a18d4ab22b92938cb8ab31 (patch)
tree596bb2f321263c89ec6fbf65e56cf770b5a122fa
parent0e1ebc3eb390e29ed31b2b9f2e8781e5824fe95f (diff)
downloadmu-87374384552f159713a18d4ab22b92938cb8ab31.tar.gz
4131
Bugfix: I hadn't been allowing continuations to be copied.

Deepens our initial sin of managing the Mu call stack implicitly in the
C interpreter. Since the call stack was implicit, continuations had to
be implicit as well. Since continuations aren't in Mu's memory, we have
to replicate refcounting logic for them.
-rw-r--r--001help.cc6
-rw-r--r--076continuation.cc52
2 files changed, 49 insertions, 9 deletions
diff --git a/001help.cc b/001help.cc
index a75657a8..1c3e062c 100644
--- a/001help.cc
+++ b/001help.cc
@@ -219,9 +219,9 @@ template<typename T> typename T::mapped_type const& get(const T& map, typename T
   return iter->second;
 }
 template<typename T> typename T::mapped_type const& put(T& map, typename T::key_type const& key, typename T::mapped_type const& value) {
-  // requires value to have a zero-arg (default) constructor
-  (*((map.insert(std::make_pair(key, typename T::mapped_type()))).first)).second = value;
-  return (*((map.insert(std::make_pair(key, typename T::mapped_type()))).first)).second;
+  // map[key] requires mapped_type to have a zero-arg (default) constructor
+  map.insert(std::make_pair(key, value)).first->second = value;
+  return value;
 }
 template<typename T> bool contains_key(T& map, typename T::key_type const& key) {
   return map.find(key) != map.end();
diff --git a/076continuation.cc b/076continuation.cc
index 8cfda4a3..4b938c62 100644
--- a/076continuation.cc
+++ b/076continuation.cc
@@ -140,8 +140,14 @@ case CALL_WITH_CONTINUATION_MARK: {
 //: save the slice of current call stack until the 'call-with-continuation-mark'
 //: call, and return it as the result.
 //: todo: implement delimited continuations in Mu's memory
+:(before "End Types")
+struct delimited_continuation {
+  call_stack frames;
+  int nrefs;
+  delimited_continuation(call_stack::iterator begin, call_stack::iterator end) :frames(call_stack(begin, end)), nrefs(0) {}
+};
 :(before "End Globals")
-map<long long int, call_stack> Delimited_continuation;
+map<long long int, delimited_continuation> Delimited_continuation;
 long long int Next_delimited_continuation_id = 1;  // 0 is null just like an address
 :(before "End Reset")
 Delimited_continuation.clear();
@@ -172,7 +178,7 @@ case RETURN_CONTINUATION_UNTIL_MARK: {
     break;
   }
   trace("run") << "creating continuation " << Next_delimited_continuation_id << end();
-  put(Delimited_continuation, Next_delimited_continuation_id, call_stack(Current_routine->calls.begin(), base));
+  put(Delimited_continuation, Next_delimited_continuation_id, delimited_continuation(Current_routine->calls.begin(), base));
   while (Current_routine->calls.begin() != base) {
     if (Trace_stream) {
       --Trace_stream->callstack_depth;
@@ -204,7 +210,7 @@ if (is_mu_continuation(current_instruction().ingredients.at(0))) {
   trace("run") << "calling continuation " << ingredients.at(0).at(0) << end();
   if (!contains_key(Delimited_continuation, ingredients.at(0).at(0)))
     raise << maybe(current_recipe_name()) << "no such delimited continuation " << current_instruction().ingredients.at(0).original_string << '\n' << end();
-  const call_stack& new_frames = get(Delimited_continuation, ingredients.at(0).at(0));
+  const call_stack& new_frames = get(Delimited_continuation, ingredients.at(0).at(0)).frames;
   const call& caller = (SIZE(new_frames) > 1) ? *++new_frames.begin() : Current_routine->calls.front();
   for (call_stack::const_reverse_iterator p = new_frames.rbegin(); p != new_frames.rend(); ++p) {
     Current_routine->calls.push_front(*p);
@@ -296,19 +302,38 @@ def g [
 # finally the alloc for main is abandoned
 +mem: automatically abandoning 1000
 
+:(before "End Increment Refcounts(canonized_x)")
+if (is_mu_continuation(canonized_x)) {
+  int continuation_id = data.at(0);
+  if (continuation_id == 0) return;
+  if (!contains_key(Delimited_continuation, continuation_id)) {
+    raise << maybe(current_recipe_name()) << "missing delimited continuation: " << canonized_x.name << '\n';
+    return;
+  }
+  delimited_continuation& curr = get(Delimited_continuation, continuation_id);
+  trace("run") << "incrementing refcount of continuation " << continuation_id << ": " << curr.nrefs << " -> " << 1+curr.nrefs << end();
+  ++curr.nrefs;
+  return;
+}
+
 :(before "End Decrement Refcounts(canonized_x)")
 if (is_mu_continuation(canonized_x)) {
   int continuation_id = get_or_insert(Memory, canonized_x.value);
-  trace("run") << "reclaiming continuation " << continuation_id << end();
   if (continuation_id == 0) return;
-  const call_stack& continuation_frames = get(Delimited_continuation, continuation_id);
+  delimited_continuation& curr = get(Delimited_continuation, continuation_id);
+  assert(curr.nrefs > 0);
+  --curr.nrefs;
+  trace("run") << "decrementing refcount of continuation " << continuation_id << ": " << 1+curr.nrefs << " -> " << curr.nrefs << end();
+  if (curr.nrefs > 0) return;
+  trace("run") << "reclaiming continuation " << continuation_id << end();
   // temporarily push the stack frames for the continuation to the call stack before reclaiming their spaces
   // (because reclaim_default_space() relies on the default-space being reclaimed being at the top of the stack)
-  for (call_stack::const_iterator p = continuation_frames.begin(); p != continuation_frames.end(); ++p) {
+  for (call_stack::const_iterator p = curr.frames.begin(); p != curr.frames.end(); ++p) {
     Current_routine->calls.push_front(*p);
     reclaim_default_space();
     Current_routine->calls.pop_front();
   }
+  Delimited_continuation.erase(continuation_id);
   return;
 }
 
@@ -317,3 +342,18 @@ bool is_mu_continuation(reagent/*copy*/ x) {
   canonize_type(x);
   return x.type && x.type->atom && x.type->value == get(Type_ordinal, "continuation");
 }
+
+:(scenario continuations_can_be_copied)
+def main [
+  local-scope
+  k:continuation <- call-with-continuation-mark f
+  k2:continuation <- copy k
+  # reclaiming k and k2 shouldn't delete f's local scope twice
+]
+def f [
+  local-scope
+  load-ingredients
+  return-continuation-until-mark
+  return 0
+]
+$error: 0