Skip to content

Commit a9a9d02

Browse files
committed
Fix issues with cleaning up Parent hierarchies
1 parent c9c8384 commit a9a9d02

File tree

7 files changed

+180
-20
lines changed

7 files changed

+180
-20
lines changed

distr/flecs.c

Lines changed: 10 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -9526,10 +9526,6 @@ void ecs_delete(
95269526
flecs_on_delete(world, ecs_pair(entity, EcsWildcard), 0, true, true);
95279527
}
95289528

9529-
if (row_flags & EcsEntityIsTraversable) {
9530-
flecs_table_traversable_add(r->table, -1);
9531-
}
9532-
95339529
/* Merge operations before deleting entity */
95349530
flecs_defer_end(world, stage);
95359531
flecs_defer_begin(world, stage);
@@ -9555,6 +9551,10 @@ void ecs_delete(
95559551
world, table, &world->store.root, row, 1, &diff);
95569552
flecs_entity_remove_non_fragmenting(world, entity, r);
95579553
flecs_table_delete(world, table, row, true);
9554+
9555+
if (row_flags & EcsEntityIsTraversable) {
9556+
flecs_table_traversable_add(table, -1);
9557+
}
95589558
}
95599559

95609560
flecs_entities_remove(world, entity);
@@ -18033,8 +18033,8 @@ bool flecs_component_mark_non_fragmenting_childof(
1803318033

1803418034
flecs_marked_id_push(world, childof_cr, EcsDelete, true);
1803518035

18036-
int32_t i, count = ecs_vec_count(&childof_cr->pair->ordered_children);
18037-
ecs_entity_t *children = ecs_vec_first(&childof_cr->pair->ordered_children);
18036+
int32_t i, count = ecs_vec_count(&pr->ordered_children);
18037+
ecs_entity_t *children = ecs_vec_first(&pr->ordered_children);
1803818038
for (i = 0; i < count; i ++) {
1803918039
ecs_entity_t e = children[i];
1804018040

@@ -18292,9 +18292,9 @@ bool flecs_on_delete_clear_entities(
1829218292
if (flecs_component_has_non_fragmenting_childof(cr)) {
1829318293
int32_t c, count = ecs_vec_count(&cr->pair->ordered_children);
1829418294
ecs_entity_t *children = ecs_vec_first(&cr->pair->ordered_children);
18295-
18295+
1829618296
ecs_defer_suspend(world);
18297-
for (c = 0; c < count; c ++) {
18297+
for (c = count - 1; c >= 0; c --) {
1829818298
ecs_delete(world, children[c]);
1829918299
}
1830018300
ecs_defer_resume(world);
@@ -40068,7 +40068,8 @@ void flecs_component_update_childof_depth(
4006840068

4006940069
EcsParent *data = tgt_table->data.columns[column - 1].data;
4007040070
ecs_entity_t parent = data[ECS_RECORD_TO_ROW(tgt_r->row)].value;
40071-
ecs_assert(parent != 0, ECS_INTERNAL_ERROR, NULL);
40071+
ecs_assert(parent != 0, ECS_CYCLE_DETECTED,
40072+
"possible cycle detected in Parent hierarchy");
4007240073

4007340074
ecs_component_record_t *cr_parent = flecs_components_get(world,
4007440075
ecs_childof(parent));

src/entity.c

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1699,10 +1699,6 @@ void ecs_delete(
16991699
flecs_on_delete(world, ecs_pair(entity, EcsWildcard), 0, true, true);
17001700
}
17011701

1702-
if (row_flags & EcsEntityIsTraversable) {
1703-
flecs_table_traversable_add(r->table, -1);
1704-
}
1705-
17061702
/* Merge operations before deleting entity */
17071703
flecs_defer_end(world, stage);
17081704
flecs_defer_begin(world, stage);
@@ -1728,6 +1724,10 @@ void ecs_delete(
17281724
world, table, &world->store.root, row, 1, &diff);
17291725
flecs_entity_remove_non_fragmenting(world, entity, r);
17301726
flecs_table_delete(world, table, row, true);
1727+
1728+
if (row_flags & EcsEntityIsTraversable) {
1729+
flecs_table_traversable_add(table, -1);
1730+
}
17311731
}
17321732

17331733
flecs_entities_remove(world, entity);

src/on_delete.c

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -276,8 +276,8 @@ bool flecs_component_mark_non_fragmenting_childof(
276276

277277
flecs_marked_id_push(world, childof_cr, EcsDelete, true);
278278

279-
int32_t i, count = ecs_vec_count(&childof_cr->pair->ordered_children);
280-
ecs_entity_t *children = ecs_vec_first(&childof_cr->pair->ordered_children);
279+
int32_t i, count = ecs_vec_count(&pr->ordered_children);
280+
ecs_entity_t *children = ecs_vec_first(&pr->ordered_children);
281281
for (i = 0; i < count; i ++) {
282282
ecs_entity_t e = children[i];
283283

@@ -535,9 +535,9 @@ bool flecs_on_delete_clear_entities(
535535
if (flecs_component_has_non_fragmenting_childof(cr)) {
536536
int32_t c, count = ecs_vec_count(&cr->pair->ordered_children);
537537
ecs_entity_t *children = ecs_vec_first(&cr->pair->ordered_children);
538-
538+
539539
ecs_defer_suspend(world);
540-
for (c = 0; c < count; c ++) {
540+
for (c = count - 1; c >= 0; c --) {
541541
ecs_delete(world, children[c]);
542542
}
543543
ecs_defer_resume(world);

src/storage/component_index.c

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1218,7 +1218,8 @@ void flecs_component_update_childof_depth(
12181218

12191219
EcsParent *data = tgt_table->data.columns[column - 1].data;
12201220
ecs_entity_t parent = data[ECS_RECORD_TO_ROW(tgt_r->row)].value;
1221-
ecs_assert(parent != 0, ECS_INTERNAL_ERROR, NULL);
1221+
ecs_assert(parent != 0, ECS_CYCLE_DETECTED,
1222+
"possible cycle detected in Parent hierarchy");
12221223

12231224
ecs_component_record_t *cr_parent = flecs_components_get(world,
12241225
ecs_childof(parent));

test/core/project.json

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -889,7 +889,11 @@
889889
"delete_with_parent_nested_4",
890890
"delete_with_parent_w_up_observer",
891891
"delete_with_parent_nested_w_up_observer",
892-
"delete_with_parent_mixed_nested_w_up_observer"
892+
"delete_with_parent_mixed_nested_w_up_observer",
893+
"delete_tree_1",
894+
"delete_tree_2",
895+
"delete_tree_3",
896+
"delete_tree_4"
893897
]
894898
}, {
895899
"id": "Hierarchies",

test/core/src/NonFragmentingChildOf.c

Lines changed: 134 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4115,3 +4115,137 @@ void NonFragmentingChildOf_delete_with_parent_mixed_nested_w_up_observer(void) {
41154115

41164116
ecs_fini(world);
41174117
}
4118+
4119+
void NonFragmentingChildOf_delete_tree_1(void) {
4120+
ecs_world_t* world = ecs_mini();
4121+
4122+
ECS_TAG(world, AlsoKill);
4123+
4124+
ecs_entity_t container = ecs_new(world);
4125+
ecs_entity_t a = ecs_new(world);
4126+
4127+
ecs_entity_t e = ecs_new(world);
4128+
ecs_set(world, e, EcsParent, {a});
4129+
4130+
ecs_add_pair(world, a, AlsoKill, e);
4131+
4132+
ecs_entity_t e_1 = ecs_new(world);
4133+
ecs_set(world, e_1, EcsParent, {e});
4134+
4135+
ecs_entity_t e_2 = ecs_new(world);
4136+
ecs_set(world, e_2, EcsParent, {e});
4137+
4138+
ecs_entity_t e_3 = ecs_new(world);
4139+
ecs_set(world, e_3, EcsParent, {e});
4140+
4141+
ecs_add_pair(world, a, EcsChildOf, container);
4142+
4143+
ecs_delete(world, e);
4144+
4145+
test_assert(!ecs_is_alive(world, e));
4146+
test_assert(!ecs_is_alive(world, e_1));
4147+
test_assert(!ecs_is_alive(world, e_2));
4148+
test_assert(!ecs_is_alive(world, e_3));
4149+
4150+
test_assert(ecs_is_alive(world, a));
4151+
test_assert(ecs_is_alive(world, container));
4152+
4153+
ecs_fini(world);
4154+
}
4155+
4156+
void NonFragmentingChildOf_delete_tree_2(void) {
4157+
ecs_world_t* world = ecs_mini();
4158+
4159+
ECS_TAG(world, AlsoKill);
4160+
4161+
ecs_entity_t container = ecs_new(world);
4162+
ecs_entity_t a = ecs_new(world);
4163+
4164+
ecs_entity_t e = ecs_new(world);
4165+
ecs_set(world, e, EcsParent, {a});
4166+
4167+
ecs_add_pair(world, a, AlsoKill, e);
4168+
4169+
ecs_entity_t e_1 = ecs_new(world);
4170+
ecs_set(world, e_1, EcsParent, {e});
4171+
4172+
ecs_entity_t e_2 = ecs_new(world);
4173+
ecs_set(world, e_2, EcsParent, {e});
4174+
4175+
ecs_entity_t e_3 = ecs_new(world);
4176+
ecs_set(world, e_3, EcsParent, {e});
4177+
4178+
ecs_defer_begin(world);
4179+
ecs_add_pair(world, a, EcsChildOf, container);
4180+
ecs_delete(world, e);
4181+
ecs_defer_end(world);
4182+
4183+
test_assert(!ecs_is_alive(world, e));
4184+
test_assert(!ecs_is_alive(world, e_1));
4185+
test_assert(!ecs_is_alive(world, e_2));
4186+
test_assert(!ecs_is_alive(world, e_3));
4187+
4188+
test_assert(ecs_is_alive(world, a));
4189+
test_assert(ecs_is_alive(world, container));
4190+
4191+
ecs_fini(world);
4192+
}
4193+
4194+
static
4195+
void on_remove_rel(ecs_iter_t* it)
4196+
{
4197+
const ecs_id_t id = it->ids[0];
4198+
for (int i = 0; i < it->count; i++) {
4199+
ecs_entity_t also_kill_ent = ecs_pair_second(it->world, id);
4200+
ecs_delete(it->world, also_kill_ent);
4201+
}
4202+
}
4203+
4204+
void NonFragmentingChildOf_delete_tree_3(void) {
4205+
ecs_world_t* world = ecs_mini();
4206+
4207+
ECS_TAG(world, Foo);
4208+
ECS_TAG(world, Rel);
4209+
4210+
ecs_observer(world, {
4211+
.query.terms = {{ ecs_pair(Rel, EcsWildcard) }},
4212+
.events = {EcsOnRemove},
4213+
.callback = on_remove_rel
4214+
});
4215+
4216+
ecs_entity_t a = ecs_new(world);
4217+
ecs_entity_t e = ecs_new(world);
4218+
ecs_entity_t e_1 = ecs_new(world);
4219+
4220+
ecs_add(world, e, Foo);
4221+
4222+
ecs_set(world, e, EcsParent, {a});
4223+
4224+
ecs_add_pair(world, a, ecs_id(Rel), e);
4225+
4226+
ecs_set(world, e_1, EcsParent, {e});
4227+
4228+
ecs_delete(world, e);
4229+
4230+
test_assert(!ecs_is_alive(world, e));
4231+
test_assert(!ecs_is_alive(world, e_1));
4232+
test_assert(ecs_is_alive(world, a));
4233+
4234+
ecs_fini(world);
4235+
}
4236+
4237+
void NonFragmentingChildOf_delete_tree_4(void) {
4238+
install_test_abort();
4239+
4240+
ecs_world_t* world = ecs_mini();
4241+
4242+
ECS_TAG(world, Rel);
4243+
4244+
ecs_entity_t a = ecs_new(world);
4245+
ecs_entity_t e = ecs_new(world);
4246+
4247+
ecs_set(world, e, EcsParent, {a});
4248+
4249+
test_expect_abort(); // cycle
4250+
ecs_set(world, a, EcsParent, {e});
4251+
}

test/core/src/main.c

Lines changed: 21 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -858,6 +858,10 @@ void NonFragmentingChildOf_delete_with_parent_nested_4(void);
858858
void NonFragmentingChildOf_delete_with_parent_w_up_observer(void);
859859
void NonFragmentingChildOf_delete_with_parent_nested_w_up_observer(void);
860860
void NonFragmentingChildOf_delete_with_parent_mixed_nested_w_up_observer(void);
861+
void NonFragmentingChildOf_delete_tree_1(void);
862+
void NonFragmentingChildOf_delete_tree_2(void);
863+
void NonFragmentingChildOf_delete_tree_3(void);
864+
void NonFragmentingChildOf_delete_tree_4(void);
861865

862866
// Testsuite 'Hierarchies'
863867
void Hierarchies_setup(void);
@@ -6441,6 +6445,22 @@ bake_test_case NonFragmentingChildOf_testcases[] = {
64416445
{
64426446
"delete_with_parent_mixed_nested_w_up_observer",
64436447
NonFragmentingChildOf_delete_with_parent_mixed_nested_w_up_observer
6448+
},
6449+
{
6450+
"delete_tree_1",
6451+
NonFragmentingChildOf_delete_tree_1
6452+
},
6453+
{
6454+
"delete_tree_2",
6455+
NonFragmentingChildOf_delete_tree_2
6456+
},
6457+
{
6458+
"delete_tree_3",
6459+
NonFragmentingChildOf_delete_tree_3
6460+
},
6461+
{
6462+
"delete_tree_4",
6463+
NonFragmentingChildOf_delete_tree_4
64446464
}
64456465
};
64466466

@@ -15325,7 +15345,7 @@ static bake_test_suite suites[] = {
1532515345
"NonFragmentingChildOf",
1532615346
NULL,
1532715347
NULL,
15328-
157,
15348+
161,
1532915349
NonFragmentingChildOf_testcases
1533015350
},
1533115351
{

0 commit comments

Comments
 (0)