Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
60 changes: 49 additions & 11 deletions src/rt/core.h
Original file line number Diff line number Diff line change
Expand Up @@ -281,14 +281,17 @@ namespace rt::core
: objects::DynObject(cownPrototypeObject(), objects::cown_region)
{
status = Status::Pending;
auto region = objects::get_region(obj);
auto old = set("value", obj);
assert(!old);
}

if (!obj->is_immutable() && !obj->is_cown())
{
// TODO: Make sure we're parenting the region and add third state, like
// pending
// Staring in an aquired state will allow the normal usage of `set`
[[nodiscard]] DynObject* set(std::string name, DynObject* obj) override
{
assert_modifiable();

if (obj && !obj->is_immutable() && !obj->is_cown())
{
auto region = objects::get_region(obj);
// Potentiall error message
std::stringstream ss;
ss << "Object is neither immutable nor a cown, attempted to threat it "
Expand All @@ -307,16 +310,23 @@ namespace rt::core
<< region->parent->bridge;
ui::error(ss.str(), {this, "", obj});
}

region->cown = this;
}

auto old = set("value", obj);
assert(!old);
DynObject* old = fields[name];
fields[name] = obj;

// 1x LRC from the stack
if (region->local_reference_count == 1)
if (old && !old->is_immutable() && !old->is_cown())
{
status = Status::Released;
auto old_reg = objects::get_region(old);
assert(old_reg->cown == this);
old_reg->cown = nullptr;
}

update_status();

return old;
}

std::string get_name() override
Expand Down Expand Up @@ -346,6 +356,34 @@ namespace rt::core
return true;
}
}

bool is_released()
{
return this->status == Status::Released;
}

/// This function updates the status of the cown. It mainly checks if a
/// cown in the pending state can be released.
void update_status()
{
if (status != Status::Pending)
{
return;
}

auto value = this->get("value").value();
if (!value || value->is_immutable() || value->is_cown())
{
status = Status::Released;
return;
}

auto region = objects::get_region(value);
if (region->combined_lrc() == 0)
{
status = Status::Released;
}
}
};

inline std::set<objects::DynObject*>* globals()
Expand Down
18 changes: 18 additions & 0 deletions src/rt/core/builtin.cc
Original file line number Diff line number Diff line change
Expand Up @@ -334,6 +334,24 @@ namespace rt::core

return std::nullopt;
});

add_builtin("is_released", [](auto frame, auto args) {
if (args != 1)
{
ui::error("is_released() expected 1 argument");
}

auto cown = frame->stack_pop("cown to check");
auto result = rt::is_cown_released(cown);
rt::remove_reference(frame->object(), cown);

auto result_obj = rt::get_bool(result);
// The return will be linked to the frame by the interpreter, but the RC
// has to be increased here.
result_obj->change_rc(1);

return result_obj;
});
}

void pragma_builtins()
Expand Down
2 changes: 1 addition & 1 deletion src/rt/objects/dyn_object.h
Original file line number Diff line number Diff line change
Expand Up @@ -268,7 +268,7 @@ namespace rt::objects
}
}

[[nodiscard]] DynObject* set(std::string name, DynObject* value)
[[nodiscard]] virtual DynObject* set(std::string name, DynObject* value)
{
assert_modifiable();
DynObject* old = fields[name];
Expand Down
29 changes: 28 additions & 1 deletion src/rt/objects/region.h
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,11 @@
#include <cassert>
#include <set>

namespace rt
{
void cown_update_state(objects::DynObject* obj);
}

namespace rt::objects
{
class DynObject;
Expand Down Expand Up @@ -49,6 +54,11 @@ namespace rt::objects
// This guarantees that the regions for trees.
Region* parent{nullptr};

// This points to the cown that owns this region. The parent will be
// filled with the cown_region if this is owned by a cown. This ensures
// that the region can't be reparented.
DynObject* cown{nullptr};

// The number of direct subregions, whose LRC is non-zero
size_t sub_region_reference_count{0};

Expand Down Expand Up @@ -81,9 +91,17 @@ namespace rt::objects
r->local_reference_count--;
// Edge triggered LRC for parent.
if (r->combined_lrc() == 0)
{
dec_sbrc(r);
if (r->cown)
{
cown_update_state(r->cown);
}
}
else
{
action(r);
}
}

// Decrements sbrc for ancestors of 'r'
Expand All @@ -96,6 +114,12 @@ namespace rt::objects
if (r->combined_lrc() != 0)
break;
}

if (r->cown)
{
cown_update_state(r->cown);
}

action(r);
}

Expand Down Expand Up @@ -149,7 +173,10 @@ namespace rt::objects
ui::error("Cycle created in region hierarchy", r->bridge);
}

p->direct_subregions.insert(r->bridge);
if (p)
{
p->direct_subregions.insert(r->bridge);
}
// Set the parent and increment the parent reference count.
r->parent = p;

Expand Down
19 changes: 19 additions & 0 deletions src/rt/rt.cc
Original file line number Diff line number Diff line change
Expand Up @@ -313,4 +313,23 @@ namespace rt
objects::dissolve_region(bridge);
}

bool is_cown_released(objects::DynObject* cown)
{
if (cown->get_prototype() != core::cownPrototypeObject())
{
ui::error("The given object is not a cown", cown);
}

return reinterpret_cast<core::CownObject*>(cown)->is_released();
}

void cown_update_state(objects::DynObject* cown)
{
if (cown->get_prototype() != core::cownPrototypeObject())
{
ui::error("The given object is not a cown", cown);
}

reinterpret_cast<core::CownObject*>(cown)->update_status();
}
} // namespace rt
5 changes: 5 additions & 0 deletions src/rt/rt.h
Original file line number Diff line number Diff line change
Expand Up @@ -64,4 +64,9 @@ namespace rt
void merge_regions(objects::DynObject* src, objects::DynObject* sink);
void dissolve_region(objects::DynObject* bridge);

/// This notifies the given cown that something has changed and the state
/// might need to be updated. This can cause a cown in the pending state to be
/// released.
void cown_update_state(objects::DynObject* cown);
bool is_cown_released(objects::DynObject* cown);
} // namespace rt
5 changes: 5 additions & 0 deletions tests/cowns/cown_from_cown.frank
Original file line number Diff line number Diff line change
Expand Up @@ -5,3 +5,8 @@ c01 = Cown(move a)

# Should succeed, cowns can contain other cowns
c02 = Cown(move c01)

if is_released(c02):
pass()
else:
unreachable()
5 changes: 5 additions & 0 deletions tests/cowns/cown_from_immutable.frank
Original file line number Diff line number Diff line change
Expand Up @@ -6,3 +6,8 @@ freeze(share)

# Should succeed, as 'share' is now immutable
c = Cown(share)

if is_released(c):
pass()
else:
unreachable()
21 changes: 21 additions & 0 deletions tests/cowns/cown_release_on_swap1.frank
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
# Creating an open region
r1 = Region()

# Creating a cown in the pending state
c1 = Cown(r1)

# Make sure the cown is still pending
if is_released(c1) == False:
pass()
else:
unreachable()

# Replacing the value with a closed region should release the cown
r2 = Region()
c1.value = move r2

# Make sure the cown is released
if is_released(c1) == True:
pass()
else:
unreachable()
20 changes: 20 additions & 0 deletions tests/cowns/cown_release_on_swap2.frank
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
# Creating an open region
r1 = Region()

# Creating a cown in the pending state
c1 = Cown(r1)

# Make sure the cown is still pending
if is_released(c1) == False:
pass()
else:
unreachable()

# Replacing the value with a frozen value should release the cown
c1.value = None

# Make sure the cown is released
if is_released(c1) == True:
pass()
else:
unreachable()
20 changes: 20 additions & 0 deletions tests/cowns/cown_release_on_swap3.frank
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
# Creating an open region
r1 = Region()

# Creating a cown in the pending state
c1 = Cown(r1)

# Make sure the cown is still pending
if is_released(c1) == False:
pass()
else:
unreachable()

# Replacing the value with a cown should release the cown
c1.value = Cown(None)

# Make sure the cown is released
if is_released(c1) == True:
pass()
else:
unreachable()
20 changes: 20 additions & 0 deletions tests/cowns/cown_released_on_close1.frank
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
# Creating an open region
r1 = Region()

# Creating a cown in the pending state
c1 = Cown(r1)

# Make sure the cown is still pending
if is_released(c1) == False:
pass()
else:
unreachable()

# Force close r1
close(r1)

# The cown should now be released
if is_released(c1) == True:
pass()
else:
unreachable()
20 changes: 20 additions & 0 deletions tests/cowns/cown_released_on_close2.frank
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
# Creating an open region
r1 = Region()

# Creating a cown in the pending state
c1 = Cown(r1)

# Make sure the cown is still pending
if is_released(c1) == False:
pass()
else:
unreachable()

# Manually close r1
r1 = None

# The cown should now be released
if is_released(c1) == True:
pass()
else:
unreachable()
1 change: 1 addition & 0 deletions tests/regressions/cown_none.frank
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
c1 = Cown(None)
5 changes: 5 additions & 0 deletions tests/regressions/cown_unknown.frank
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
c1 = Cown(None)
r1 = Region()
c2 = Cown(c1)