Skip to content

Commit

Permalink
Fix issue with invoking observers/hooks while deferring is suspended
Browse files Browse the repository at this point in the history
  • Loading branch information
SanderMertens committed Sep 30, 2023
1 parent 50e0390 commit 1771406
Show file tree
Hide file tree
Showing 6 changed files with 182 additions and 2 deletions.
15 changes: 15 additions & 0 deletions flecs.c
Original file line number Diff line number Diff line change
Expand Up @@ -4405,6 +4405,11 @@ void flecs_invoke_hook(
ecs_entity_t event,
ecs_iter_action_t hook)
{
int32_t defer = world->stages[0].defer;
if (defer < 0) {
world->stages[0].defer *= -1;
}

ecs_iter_t it = { .field_count = 1};
it.entities = entities;

Expand All @@ -4424,6 +4429,8 @@ void flecs_invoke_hook(
flecs_iter_validate(&it);
hook(&it);
ecs_iter_fini(&it);

world->stages[0].defer = defer;
}

void flecs_notify_on_set(
Expand Down Expand Up @@ -14904,6 +14911,12 @@ void flecs_emit(
int32_t i, r, count = desc->count;
ecs_flags32_t table_flags = table->flags;

/* Deferring cannot be suspended for observers */
int32_t defer = world->stages[0].defer;
if (defer < 0) {
world->stages[0].defer *= -1;
}

/* Table events are emitted for internal table operations only, and do not
* provide component data and/or entity ids. */
bool table_event = desc->flags & EcsEventTableOnly;
Expand Down Expand Up @@ -15241,6 +15254,8 @@ void flecs_emit(
}

error:
world->stages[0].defer = defer;

if (measure_time) {
world->info.emit_time_total += (ecs_ftime_t)ecs_time_measure(&t);
}
Expand Down
7 changes: 7 additions & 0 deletions src/entity.c
Original file line number Diff line number Diff line change
Expand Up @@ -1033,6 +1033,11 @@ void flecs_invoke_hook(
ecs_entity_t event,
ecs_iter_action_t hook)
{
int32_t defer = world->stages[0].defer;
if (defer < 0) {
world->stages[0].defer *= -1;
}

ecs_iter_t it = { .field_count = 1};
it.entities = entities;

Expand All @@ -1052,6 +1057,8 @@ void flecs_invoke_hook(
flecs_iter_validate(&it);
hook(&it);
ecs_iter_fini(&it);

world->stages[0].defer = defer;
}

void flecs_notify_on_set(
Expand Down
8 changes: 8 additions & 0 deletions src/observable.c
Original file line number Diff line number Diff line change
Expand Up @@ -1000,6 +1000,12 @@ void flecs_emit(
int32_t i, r, count = desc->count;
ecs_flags32_t table_flags = table->flags;

/* Deferring cannot be suspended for observers */
int32_t defer = world->stages[0].defer;
if (defer < 0) {
world->stages[0].defer *= -1;
}

/* Table events are emitted for internal table operations only, and do not
* provide component data and/or entity ids. */
bool table_event = desc->flags & EcsEventTableOnly;
Expand Down Expand Up @@ -1337,6 +1343,8 @@ void flecs_emit(
}

error:
world->stages[0].defer = defer;

if (measure_time) {
world->info.emit_time_total += (ecs_ftime_t)ecs_time_measure(&t);
}
Expand Down
6 changes: 5 additions & 1 deletion test/api/project.json
Original file line number Diff line number Diff line change
Expand Up @@ -2549,7 +2549,11 @@
"defer_2_sets_w_observer_same_component",
"defer_2_sets_w_observer_other_component",
"on_remove_after_deferred_clear_and_add",
"defer_delete_recycle_same_id"
"defer_delete_recycle_same_id",
"observer_while_defer_suspended",
"on_add_hook_while_defer_suspended",
"on_set_hook_while_defer_suspended",
"on_remove_hook_while_defer_suspended"
]
}, {
"id": "SingleThreadStaging",
Expand Down
126 changes: 126 additions & 0 deletions test/api/src/DeferredActions.c
Original file line number Diff line number Diff line change
Expand Up @@ -3446,3 +3446,129 @@ void DeferredActions_defer_delete_recycle_same_id(void) {

ecs_fini(world);
}

static ECS_COMPONENT_DECLARE(Velocity);

static
void AddWhileSuspended(ecs_iter_t *it) {
for (int i = 0; i < it->count; i ++) {
ecs_add(it->world, it->entities[i], Velocity);
}
}

void DeferredActions_observer_while_defer_suspended(void) {
ecs_world_t *world = ecs_mini();

ECS_COMPONENT(world, Position);
ECS_COMPONENT_DEFINE(world, Velocity);

ecs_observer(world, {
.filter.terms = {
{ .id = ecs_id(Position) }
},
.events = { EcsOnAdd },
.callback = AddWhileSuspended,
});

ecs_defer_begin(world);
ecs_defer_suspend(world);

ecs_entity_t e = ecs_new(world, Position);
test_assert(e != 0);
test_assert(ecs_has(world, e, Position));
test_assert(!ecs_has(world, e, Velocity));

ecs_defer_resume(world);
ecs_defer_end(world);

test_assert(ecs_has(world, e, Position));
test_assert(ecs_has(world, e, Velocity));

ecs_fini(world);
}

void DeferredActions_on_add_hook_while_defer_suspended(void) {
ecs_world_t *world = ecs_mini();

ECS_COMPONENT(world, Position);
ECS_COMPONENT_DEFINE(world, Velocity);

ecs_set_hooks(world, Position, {
.on_add = AddWhileSuspended
});

ecs_defer_begin(world);
ecs_defer_suspend(world);

ecs_entity_t e = ecs_new(world, Position);
test_assert(e != 0);
test_assert(ecs_has(world, e, Position));
test_assert(!ecs_has(world, e, Velocity));

ecs_defer_resume(world);
ecs_defer_end(world);

test_assert(ecs_has(world, e, Position));
test_assert(ecs_has(world, e, Velocity));

ecs_fini(world);
}

void DeferredActions_on_set_hook_while_defer_suspended(void) {
ecs_world_t *world = ecs_mini();

ECS_COMPONENT(world, Position);
ECS_COMPONENT_DEFINE(world, Velocity);

ecs_set_hooks(world, Position, {
.on_set = AddWhileSuspended
});

ecs_defer_begin(world);
ecs_defer_suspend(world);

ecs_entity_t e = ecs_set(world, 0, Position, {10, 20});
test_assert(e != 0);
test_assert(ecs_has(world, e, Position));
test_assert(!ecs_has(world, e, Velocity));

ecs_defer_resume(world);
ecs_defer_end(world);

test_assert(ecs_has(world, e, Position));
test_assert(ecs_has(world, e, Velocity));

ecs_fini(world);
}

void DeferredActions_on_remove_hook_while_defer_suspended(void) {
ecs_world_t *world = ecs_mini();

ECS_COMPONENT(world, Position);
ECS_COMPONENT_DEFINE(world, Velocity);

ecs_set_hooks(world, Position, {
.on_remove = AddWhileSuspended
});

ecs_entity_t e = ecs_new(world, Position);
test_assert(e != 0);
test_assert(ecs_has(world, e, Position));
test_assert(!ecs_has(world, e, Velocity));

ecs_defer_begin(world);
ecs_defer_suspend(world);

test_assert(ecs_has(world, e, Position));
ecs_remove(world, e, Position);
test_assert(!ecs_has(world, e, Position));
test_assert(!ecs_has(world, e, Velocity));

ecs_defer_resume(world);
ecs_defer_end(world);

test_assert(!ecs_has(world, e, Position));
test_assert(ecs_has(world, e, Velocity));

ecs_fini(world);
}
22 changes: 21 additions & 1 deletion test/api/src/main.c
Original file line number Diff line number Diff line change
Expand Up @@ -2459,6 +2459,10 @@ void DeferredActions_defer_2_sets_w_observer_same_component(void);
void DeferredActions_defer_2_sets_w_observer_other_component(void);
void DeferredActions_on_remove_after_deferred_clear_and_add(void);
void DeferredActions_defer_delete_recycle_same_id(void);
void DeferredActions_observer_while_defer_suspended(void);
void DeferredActions_on_add_hook_while_defer_suspended(void);
void DeferredActions_on_set_hook_while_defer_suspended(void);
void DeferredActions_on_remove_hook_while_defer_suspended(void);

// Testsuite 'SingleThreadStaging'
void SingleThreadStaging_setup(void);
Expand Down Expand Up @@ -12164,6 +12168,22 @@ bake_test_case DeferredActions_testcases[] = {
{
"defer_delete_recycle_same_id",
DeferredActions_defer_delete_recycle_same_id
},
{
"observer_while_defer_suspended",
DeferredActions_observer_while_defer_suspended
},
{
"on_add_hook_while_defer_suspended",
DeferredActions_on_add_hook_while_defer_suspended
},
{
"on_set_hook_while_defer_suspended",
DeferredActions_on_set_hook_while_defer_suspended
},
{
"on_remove_hook_while_defer_suspended",
DeferredActions_on_remove_hook_while_defer_suspended
}
};

Expand Down Expand Up @@ -13074,7 +13094,7 @@ static bake_test_suite suites[] = {
"DeferredActions",
NULL,
NULL,
116,
120,
DeferredActions_testcases
},
{
Expand Down

0 comments on commit 1771406

Please sign in to comment.