Skip to content

Commit a9dc41d

Browse files
committed
Cowns: Releasing pending cowns and less seg faults
1 parent 4133ff6 commit a9dc41d

15 files changed

+237
-13
lines changed

src/rt/core.h

+49-11
Original file line numberDiff line numberDiff line change
@@ -281,14 +281,17 @@ namespace rt::core
281281
: objects::DynObject(cownPrototypeObject(), objects::cown_region)
282282
{
283283
status = Status::Pending;
284-
auto region = objects::get_region(obj);
284+
auto old = set("value", obj);
285+
assert(!old);
286+
}
285287

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

292+
if (obj && !obj->is_immutable() && !obj->is_cown())
293+
{
294+
auto region = objects::get_region(obj);
292295
// Potentiall error message
293296
std::stringstream ss;
294297
ss << "Object is neither immutable nor a cown, attempted to threat it "
@@ -307,16 +310,23 @@ namespace rt::core
307310
<< region->parent->bridge;
308311
ui::error(ss.str(), {this, "", obj});
309312
}
313+
314+
region->cown = this;
310315
}
311316

312-
auto old = set("value", obj);
313-
assert(!old);
317+
DynObject* old = fields[name];
318+
fields[name] = obj;
314319

315-
// 1x LRC from the stack
316-
if (region->local_reference_count == 1)
320+
if (old && !old->is_immutable() && !old->is_cown())
317321
{
318-
status = Status::Released;
322+
auto old_reg = objects::get_region(old);
323+
assert(old_reg->cown == this);
324+
old_reg->cown = nullptr;
319325
}
326+
327+
update_status();
328+
329+
return old;
320330
}
321331

322332
std::string get_name() override
@@ -346,6 +356,34 @@ namespace rt::core
346356
return true;
347357
}
348358
}
359+
360+
bool is_released()
361+
{
362+
return this->status == Status::Released;
363+
}
364+
365+
/// This function updates the status of the cown. It mainly checks if a
366+
/// cown in the pending state can be released.
367+
void update_status()
368+
{
369+
if (status != Status::Pending)
370+
{
371+
return;
372+
}
373+
374+
auto value = this->get("value").value();
375+
if (!value || value->is_immutable() || value->is_cown())
376+
{
377+
status = Status::Released;
378+
return;
379+
}
380+
381+
auto region = objects::get_region(value);
382+
if (region->combined_lrc() == 0)
383+
{
384+
status = Status::Released;
385+
}
386+
}
349387
};
350388

351389
inline std::set<objects::DynObject*>* globals()

src/rt/core/builtin.cc

+18
Original file line numberDiff line numberDiff line change
@@ -334,6 +334,24 @@ namespace rt::core
334334

335335
return std::nullopt;
336336
});
337+
338+
add_builtin("is_released", [](auto frame, auto args) {
339+
if (args != 1)
340+
{
341+
ui::error("is_released() expected 1 argument");
342+
}
343+
344+
auto cown = frame->stack_pop("cown to check");
345+
auto result = rt::is_cown_released(cown);
346+
rt::remove_reference(frame->object(), cown);
347+
348+
auto result_obj = rt::get_bool(result);
349+
// The return will be linked to the frame by the interpreter, but the RC
350+
// has to be increased here.
351+
result_obj->change_rc(1);
352+
353+
return result_obj;
354+
});
337355
}
338356

339357
void pragma_builtins()

src/rt/objects/dyn_object.h

+1-1
Original file line numberDiff line numberDiff line change
@@ -268,7 +268,7 @@ namespace rt::objects
268268
}
269269
}
270270

271-
[[nodiscard]] DynObject* set(std::string name, DynObject* value)
271+
[[nodiscard]] virtual DynObject* set(std::string name, DynObject* value)
272272
{
273273
assert_modifiable();
274274
DynObject* old = fields[name];

src/rt/objects/region.h

+28-1
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,11 @@
66
#include <cassert>
77
#include <set>
88

9+
namespace rt
10+
{
11+
void cown_update_state(objects::DynObject* obj);
12+
}
13+
914
namespace rt::objects
1015
{
1116
class DynObject;
@@ -49,6 +54,11 @@ namespace rt::objects
4954
// This guarantees that the regions for trees.
5055
Region* parent{nullptr};
5156

57+
// This points to the cown that owns this region. The parent will be
58+
// filled with the cown_region if this is owned by a cown. This ensures
59+
// that the region can't be reparented.
60+
DynObject* cown{nullptr};
61+
5262
// The number of direct subregions, whose LRC is non-zero
5363
size_t sub_region_reference_count{0};
5464

@@ -81,9 +91,17 @@ namespace rt::objects
8191
r->local_reference_count--;
8292
// Edge triggered LRC for parent.
8393
if (r->combined_lrc() == 0)
94+
{
8495
dec_sbrc(r);
96+
if (r->cown)
97+
{
98+
cown_update_state(r->cown);
99+
}
100+
}
85101
else
102+
{
86103
action(r);
104+
}
87105
}
88106

89107
// Decrements sbrc for ancestors of 'r'
@@ -96,6 +114,12 @@ namespace rt::objects
96114
if (r->combined_lrc() != 0)
97115
break;
98116
}
117+
118+
if (r->cown)
119+
{
120+
cown_update_state(r->cown);
121+
}
122+
99123
action(r);
100124
}
101125

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

152-
p->direct_subregions.insert(r->bridge);
176+
if (p)
177+
{
178+
p->direct_subregions.insert(r->bridge);
179+
}
153180
// Set the parent and increment the parent reference count.
154181
r->parent = p;
155182

src/rt/rt.cc

+19
Original file line numberDiff line numberDiff line change
@@ -313,4 +313,23 @@ namespace rt
313313
objects::dissolve_region(bridge);
314314
}
315315

316+
bool is_cown_released(objects::DynObject* cown)
317+
{
318+
if (cown->get_prototype() != core::cownPrototypeObject())
319+
{
320+
ui::error("The given object is not a cown", cown);
321+
}
322+
323+
return reinterpret_cast<core::CownObject*>(cown)->is_released();
324+
}
325+
326+
void cown_update_state(objects::DynObject* cown)
327+
{
328+
if (cown->get_prototype() != core::cownPrototypeObject())
329+
{
330+
ui::error("The given object is not a cown", cown);
331+
}
332+
333+
reinterpret_cast<core::CownObject*>(cown)->update_status();
334+
}
316335
} // namespace rt

src/rt/rt.h

+5
Original file line numberDiff line numberDiff line change
@@ -64,4 +64,9 @@ namespace rt
6464
void merge_regions(objects::DynObject* src, objects::DynObject* sink);
6565
void dissolve_region(objects::DynObject* bridge);
6666

67+
/// This notifies the given cown that something has changed and the state
68+
/// might need to be updated. This can cause a cown in the pending state to be
69+
/// released.
70+
void cown_update_state(objects::DynObject* cown);
71+
bool is_cown_released(objects::DynObject* cown);
6772
} // namespace rt

tests/cowns/cown_from_cown.frank

+5
Original file line numberDiff line numberDiff line change
@@ -5,3 +5,8 @@ c01 = Cown(move a)
55

66
# Should succeed, cowns can contain other cowns
77
c02 = Cown(move c01)
8+
9+
if is_released(c02):
10+
pass()
11+
else:
12+
unreachable()

tests/cowns/cown_from_immutable.frank

+5
Original file line numberDiff line numberDiff line change
@@ -6,3 +6,8 @@ freeze(share)
66

77
# Should succeed, as 'share' is now immutable
88
c = Cown(share)
9+
10+
if is_released(c):
11+
pass()
12+
else:
13+
unreachable()
+21
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
# Creating an open region
2+
r1 = Region()
3+
4+
# Creating a cown in the pending state
5+
c1 = Cown(r1)
6+
7+
# Make sure the cown is still pending
8+
if is_released(c1) == False:
9+
pass()
10+
else:
11+
unreachable()
12+
13+
# Replacing the value with a closed region should release the cown
14+
r2 = Region()
15+
c1.value = move r2
16+
17+
# Make sure the cown is released
18+
if is_released(c1) == True:
19+
pass()
20+
else:
21+
unreachable()
+20
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
# Creating an open region
2+
r1 = Region()
3+
4+
# Creating a cown in the pending state
5+
c1 = Cown(r1)
6+
7+
# Make sure the cown is still pending
8+
if is_released(c1) == False:
9+
pass()
10+
else:
11+
unreachable()
12+
13+
# Replacing the value with a frozen value should release the cown
14+
c1.value = None
15+
16+
# Make sure the cown is released
17+
if is_released(c1) == True:
18+
pass()
19+
else:
20+
unreachable()
+20
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
# Creating an open region
2+
r1 = Region()
3+
4+
# Creating a cown in the pending state
5+
c1 = Cown(r1)
6+
7+
# Make sure the cown is still pending
8+
if is_released(c1) == False:
9+
pass()
10+
else:
11+
unreachable()
12+
13+
# Replacing the value with a cown should release the cown
14+
c1.value = Cown(None)
15+
16+
# Make sure the cown is released
17+
if is_released(c1) == True:
18+
pass()
19+
else:
20+
unreachable()
+20
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
# Creating an open region
2+
r1 = Region()
3+
4+
# Creating a cown in the pending state
5+
c1 = Cown(r1)
6+
7+
# Make sure the cown is still pending
8+
if is_released(c1) == False:
9+
pass()
10+
else:
11+
unreachable()
12+
13+
# Force close r1
14+
close(r1)
15+
16+
# The cown should now be released
17+
if is_released(c1) == True:
18+
pass()
19+
else:
20+
unreachable()
+20
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
# Creating an open region
2+
r1 = Region()
3+
4+
# Creating a cown in the pending state
5+
c1 = Cown(r1)
6+
7+
# Make sure the cown is still pending
8+
if is_released(c1) == False:
9+
pass()
10+
else:
11+
unreachable()
12+
13+
# Manually close r1
14+
r1 = None
15+
16+
# The cown should now be released
17+
if is_released(c1) == True:
18+
pass()
19+
else:
20+
unreachable()

tests/regressions/cown_none.frank

+1
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
c1 = Cown(None)

tests/regressions/cown_unknown.frank

+5
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
c1 = Cown(None)
2+
r1 = Region()
3+
c2 = Cown(c1)
4+
5+

0 commit comments

Comments
 (0)