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.
This commit is contained in:
Kartik K. Agaram 2017-11-19 04:23:31 -08:00
parent 0e1ebc3eb3
commit 8737438455
2 changed files with 49 additions and 9 deletions

View File

@ -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();

View File

@ -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